1

I'm currently working on a program that encrypts (using Caesar cipher) a user input string by replacing specific letters with custom preset letters. For instance A = R, B = T, C = O, etc.

The current program:

using System;
class Program
{
    static void Main(string[] args)
    {
        String decryptedInput = Console.ReadLine().ToUpper();
        String encryptedOutput = decryptedInput.Replace("A", "R")
                                        .Replace("B", "B")
                                        .Replace("C", "T")
                                        .Replace("D", "O")
                                        .Replace("E", "P")
                                        .Replace("F", "M")
                                        .Replace("G", "Z")
                                        .Replace("H", "S")
                                        .Replace("I", "J")
                                        .Replace("J", "K")
                                        .Replace("K", "I")
                                        .Replace("L", "Y")
                                        .Replace("M", "P")
                                        .Replace("N", "G")
                                        .Replace("O", "L")
                                        .Replace("P", "V")
                                        .Replace("Q", "C")
                                        .Replace("R", "X")
                                        .Replace("S", "N")
                                        .Replace("T", "E")
                                        .Replace("U", "H")
                                        .Replace("V", "F")
                                        .Replace("P", "A")
                                        .Replace("X", "U")
                                        .Replace("Y", "Q")
                                        .Replace("Z", "D");
        Console.WriteLine(encryptedOutput);
        Console.ReadKey();
    }
}

I do get the output, but there are some encryption errors. The problem is, that since the lines of code run after each other, letters that are already converted are converted again.

For example: The letter "A" is encrypted/converted to "R". When the program gets to the letter "R", the letter is encrypted/converted again and ends up being "X", which gets converted to "U" later in the code. This happens to almost every letter and then I end up with an encrypted text, which I can never decrypt.

Is there any way to replace all of the letters at the same time, or would you simply recommend me using another function?

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
bezunyl
  • 61
  • 6
  • 1
    https://stackoverflow.com/questions/7265315/replace-multiple-characters-in-a-c-sharp-string – kelalaka Dec 18 '18 at 22:54
  • Is there a circular mapping between any of the letters? If not, then you can map out all your letters in conversion order, and do the replacement in backwards order. But it looks like the whole mapping is a circle. – Nathan Werry Dec 18 '18 at 22:54
  • Use `StringBuilder.Replace`, not `String.Replace`. It does the replace in the StringBuilder's buffer. String.Replace creates a new string on each operation (since strings are immutable). – Flydog57 Dec 18 '18 at 23:20
  • https://www.bing.com/search?q=c%23%20caesar%20cipher%20site%3Astackoverflow.com will give you more versions of code to stare at... – Alexei Levenkov Dec 19 '18 at 02:52
  • You have `P` instead of `W` on both sides of replacing, that is not good. – Antonín Lejsek Dec 19 '18 at 03:52

5 Answers5

2

Simplest representation of what you want to do is here

var dict = new Dictionary<Char, Char>();
// load dictionary here

var original = "ABC";
var newOne = new StringBuilder();
foreach (var c in original)
    newOne.Append(dict[c]);

return newOne.ToString();

This is pseudo-code. But this is simple enough for you to understand that you need to build new string because strings are immutable

I posted Dictionary<string, string> - it can be searched in reverse. You can use List<T>.

 class ED 
 {
     public Char Enc {get; set;}
     public Char Dec {get; set;}
 }
 var list = new List<ED>();

Now you can be clearer, when you encrypt/decrypt. For encrypt do list.First(c => c.Enc.Equals(inputChar)) and for decrypt list.First(c => c.Dec.Equals(inputChar))

T.S.
  • 18,195
  • 11
  • 58
  • 78
  • Can't use `char` as key for a dictionary of string to string. – Xiaoy312 Dec 18 '18 at 23:13
  • @Xiaoy312 sure - this is pseudo code. Just the idea that you don't create new string for every `replace` but rather build your new string – T.S. Dec 18 '18 at 23:15
  • This is a very efficient way to do it - you pass through the string once, and only pay for a Dictionary lookup on each iteration. – Flydog57 Dec 18 '18 at 23:23
2

Like other have mentioned, you replaced some character multiples time. This is likely not the desired behavior:

eg: A -> R -> X -> U

Also, there seems to be a typo around those lines:

.Replace("V", "F")
.Replace("P", "A")   // should this be W->A ?
.Replace("X", "U")

All these may have contributed to your problem.

In order to prevent this, just make a single pass to replace the string:

// build up character mappings for decryption
const string EncryptedLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const string DecryptedLetters = "RBTOPMZSJKIYPGLVCXNEHFAUQD";
var decryptionMapping = EncryptedLetters.Zip(DecryptedLetters, Tuple.Create)
    .ToDictionary(x => x.Item1, x => x.Item2);

// decrypt the message
var encryptedMessage = "CTHC";
var decryptedMessage = encryptedMessage
    .Select(x => decryptionMapping[x])
    .Aggregate(new StringBuilder(), (sb, x) => sb.Append(x), sb => sb.ToString());
Xiaoy312
  • 14,292
  • 1
  • 32
  • 44
1

I will help you with the outside, finding out how it works is a nice exercise in Linq and the rest of the library.

static void Main(string[] args)
{    
    string decryptedInput = "Hello World!";
    string encryptedOutput = new string(decryptedInput.Select(EncryptChar).ToArray());
    Console.WriteLine(encryptedOutput);
}

private static char EncryptChar(char arg)
{
    return arg;
}

Your problem is now reduced to writing a better EncryptChar()

H H
  • 263,252
  • 30
  • 330
  • 514
0

If you want to keep with your simple replace strategy that you have implemented, instead of doing the dictionary obfuscation that @T.S. demonstrated, you can do the following, your map has 3 distinct groups that needs this treatment, hence the whitespacing.

  1. Sort your map by connections
  2. Break any loops by mapping one to an external value
  3. Process your map backwards
  4. Set step 2 to correct value

Code:

var encryptedOutput = decryptedInput
    .Replace("A", "AA")
    .Replace("P", "A")
    .Replace("M", "P")
    .Replace("F", "M")
    .Replace("V", "F")
    .Replace("P", "V")
    .Replace("E", "P")
    .Replace("T", "E")
    .Replace("C", "T")
    .Replace("Q", "C")
    .Replace("Y", "Q")
    .Replace("L", "Y")
    .Replace("O", "L")
    .Replace("D", "O")
    .Replace("Z", "D")
    .Replace("G", "Z")
    .Replace("N", "G")
    .Replace("S", "N")
    .Replace("H", "S")
    .Replace("U", "H")
    .Replace("X", "U")
    .Replace("R", "X")
    .Replace("AA", "R")

    .Replace("B", "B")

    .Replace("I", "II")
    .Replace("K", "I")
    .Replace("J", "K")
    .Replace("II", "J");
Nathan Werry
  • 876
  • 7
  • 18
0

I would do this using a Dictionary and linq. Note that any characters (numbers, spaces, symbols) not in the dictionary will not be converted. When I do the lookup against the Dictionary, notice how I set the output char to the current char value of the foreach. Also, since this is case sensitive, I have to convert the string to uppercase.

public class Program
{

    public static void Main(string[] args)
    {           

     string encryptedOutput = "";
        var decryptedInput = "this is a test string";
        Dictionary<char,char> cipherTable = 
        new Dictionary<char,char>{
            {'A', 'R'},
            {'B', 'B'},
            {'C', 'T'},
            {'D', 'O'},
            {'E', 'P'},
            {'F', 'M'},
            {'G', 'Z'},
            {'H', 'S'},
            {'I', 'J'},
            {'J', 'K'},
            {'K', 'I'},
            {'L', 'Y'},
            {'M', 'P'},
            {'N', 'G'},
            {'O', 'L'},
            {'P', 'V'},
            {'Q', 'C'},
            {'R', 'X'},
            {'S', 'N'},
            {'T', 'E'},
            {'U', 'H'},
            {'V', 'F'},
            {'W', 'A'},
            {'X', 'U'},
            {'Y', 'Q'},
            {'Z', 'D'}
        };         
        encryptedOutput = string.Join("",decryptedInput
                                        .ToUpper()
                                        .ToArray()
                                        .Select(c => {char outChar = c; cipherTable.TryGetValue(c, out outChar); return outChar;}));
        Console.WriteLine(encryptedOutput);

    }
}
Michael
  • 1,556
  • 13
  • 25