John.Smith
John.Smith

Reputation: 87

Replacing Characters Simultaneously

Hey guys I'm trying to make a program that helps people encrypt messages and decrypt messages using the Caesar shift cipher, I know it's probably already been done, I want to have a go myself though.

The problem I've been having is when it comes to encrypting the text. The user selects a number (between 1-25) and then the application will change the letters corresponding to the number chosen, e.g. if the user inputs "HI" and selects 2, both characters are moved two places down the alphabet outputting "JK". My main problem is the replacing characters though, mostly because I've set up the program to be able to encrypt large blocks of text, because my code is:

    If cmbxKey.Text = "1" Then
        If txtOutput.Text.Contains("a") Then
            sOutput = txtOutput.Text.Replace("a", "b")
            txtOutput.Text = sOutput
        End If

        If txtOutput.Text.Contains("b") Then
            sOutput = txtOutput.Text.Replace("b", "c")
            txtOutput.Text = sOutput
        End If
    End If

This means if the user inputs "HAY" it will change it to "HBY" and then because of the second if statement it will change it to "HCY" but I only want it to be changed once. Any suggestions to avoid this???? Thanks guys

Upvotes: 1

Views: 139

Answers (3)

Karl Stephen
Karl Stephen

Reputation: 1140

One big issue arise when you're facing a String that the basic Alphanumeric table can't handle. A String that contains words like : "Déja vu" -> The "é" is going to be what ?

And also, how about encoding the string "I'm Aaron Mbilébé" if you use .ToUpper().
.ToUpper returns "I'M AARON MBILÉBÉ".
You've lost the casing, and how do you handle the shifting of "É" ?

Of course, a code should be smart as pointed above, and I was used to deal with strings just by using the System.Text.ASCIIEncoding to make things easier. But from the moment I started to use large amount of textual datas, sources from the web, files (...) I was forced to dig deeper, and seriously consider string encoding (and System Endianness by the way, when coding and decoding string to/from array of bytes)

Re-think of what do you really want in the end. If you're the only one to use your code, and you're certain that you'll only use A..Z, 0..9, a..z, space and a fixed amount of allowed characters (like puntuation) then, just build a Table containing each of those chars.

Private _AllowedChars As Char() = { "A"c, "B"c, ... "0"c, "1"c, .. "."c, ","c ... }

or

Private _AllowedChars As Char() = "ABCDEF....012...abcd..xyz.;,?:/".ToCharArray()

Then use

Private Function ShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
    Dim AllChars As Char() = CurrentString.ToCharArray()
    Dim FinalChars As Char()
    Dim i As Integer

    FinalChars = New Char(AllChars.Length - 1) {} ' It's VB : UpperBound is n+1 item.
    ' so n items is UpperBound - 1
    For i = 0 To AllChars.Length - 1
        FinalChars(i) = _AllowedChars((Array.IndexOf(_AllowedChars, AllChars(i)) + ShiftValue) Mod _AllowedChars.Length)
    Next
    Return New String(FinalChars)
End Function

And

Private Function UnShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
    ' ... the same code until :
        FinalChars(i) = _AllowedChars((Array.IndexOf(_AllowedChars, AllChars(i)) - ShiftValue + _AllowedChars.Length) Mod _AllowedChars.Length)
    ' ...
End Function

^^ Assuming ShiftValue is always positive (defined once)

But again, this only works when you have a predefined set of allowed characters. If you want a more flexible tool, you ought to start dealing with encodings, array of byte, BitConverter and have a look at system endianness. That's why I asked if someone else is goind to use your application : let's try this string :

"Xin chào thế giới" ' which is Hello World in vietnamese (Google Trad)

In that case, you may give up..? No ! You ALWAYS have a trick in your cards !
Just create your allowed chars on the fly

Private _AllowedChars As New SortedList(Of Char, Char)

-> get the string to encode (shift)

Private Function ShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
    Dim AllChars As Char() = CurrentString.ToCharArray()
    Dim FinalChars As Char()
    Dim i As Integer

    ' Build your list of allowed chars...
    _AllowedChars.Clear()
    For i = 0 To AllChars.Length - 1
        If Not _AllowedChars.ContainsKey(AllChars(i)) Then
            _AllowedChars.Add(AllChars(i), AllChars(i))
        End If
    Next

    ' Then, encode...
    FinalChars = New Char(AllChars.Length - 1) {}
    For i = 0 To AllChars.Length - 1
        FinalChars(i) = _AllowedChars.Keys.Item((_AllowedChars.IndexOfKey(AllChars(i)) + ShiftValue) Mod _AllowedChars.Count)
    Next
    Return New String(FinalChars)
End Function

The same for Unshift/decode.
Note : in foreing languages, the resulting string is pure garbage and totally unreadable, unless you (un)shift the chars again.

However, the main limitation of this workaround is the same as the fixed chars array above : Once you encode your string, and add a char in your encoded string that doesn't exists in the initial generated allowed chars, then you've nuked your data and you won't be able to decode your string. All you'll have is pure garbage.

So one day... one day maybe, you'll have to dig deeper at the byte level of the thing, in a defined extended encoding (Unicode/UTF8/16) to secure the integrity of your data.

Upvotes: 0

Maarten Bodewes
Maarten Bodewes

Reputation: 94028

A very simple method of changing your application while keeping your strategy is to replace the lower case characters with upper case characters. Then they won't be recognized by the Replace method anymore.

Obviously, the problem is that you want to implement an algorithm. In general, an algorithm should be smart in the sense that you don't have to do the grunt work. That's why a method such as the one presented by Steve is smarter; it doesn't require you to map each character separately, which is tedious, and - as most tedious tasks - error prone.

Upvotes: 0

Steve
Steve

Reputation: 5545

Since you want to shift all characters, start out by looping though the characters using something like ToArray:

For each s as string in txtOutput.Text.ToArray
  'This will be here for each character in the string, even spaces
Next

Then, rather than having cases for every letter, look at it's ascii number:

ACS(s)

...and shift it by the number you want to. Keep in mind that if the number is greater than (I don't know if you want upper/lower case) 122, you want to subtract 65 to get you back to "A".

Then you can convert it back into a character using:

CHR(?)

So this might look something like this:

Dim sb as new text.StringBuilder()
For each s as string in txtOutput.Text.ToArray
  If asc(s) > 122 Then
     sb.append(CHR(ASC(s) + ?YourShift? - 65)
  Else
    sb.append(CHR(ASC(s) + ?YourShift?)
  END IF
Next
txtOutput.Text = sb.ToString

Upvotes: 2

Related Questions