3

I am creating a Caesar Cipher and I'm trying to figure out how to make it work with both uppercase and lowercase letters. For Example if I type in "Hello World" I want it to encrypt in the same format, "Ifmmp Xpsme.

using System;

namespace CaesarCipher1
{
    class Program
    {
        static string Encrypt(string value, int shift)
        {
            shift %= 26;
            char[] alphabet = value.ToCharArray();
            for (int i = 0; i < alphabet.Length; i++)
            {
                char letter = alphabet[i];
                if (letter == ' ')
                    continue;

                letter = (char)(letter + shift);
                if (letter > 'z')
                {
                    letter = (char)(letter - 26);
                }
                else if (letter < 'a')
                {
                    letter = (char)(letter + 26);
                }

                // Store.
                alphabet[i] = letter;
            }
            return new string(alphabet);
        }
        static string Decrypt(string value, int shift)
        {
            return Encrypt(value, 26 - shift);
        }

        static void Main(string[] args)
        {
            bool Continue = true;

            Console.WriteLine("      Ceasar Cipher");
            Console.WriteLine("-------------------------\n");

            while (Continue)
            {
                try
                {
                    Console.WriteLine("\nType a string to encrypt:");
                    string UserString = Console.ReadLine();

                    Console.Write("\nShift: ");
                    int key = Convert.ToInt32(Console.ReadLine());

                    Console.WriteLine("\nEncrypted Data: ");

                    string cipherText = Encrypt(UserString, key);
                    Console.WriteLine(cipherText);
                    Console.Write("\n");

                    Console.WriteLine("Decrypted Data:");

                    string t = Decrypt(cipherText, key);
                    Console.WriteLine(t);

                    Console.WriteLine("\nDo you want to continue?");
                    Console.WriteLine("Type in Yes to continue or press any other key and then press enter to quit:");
                    string response = Console.ReadLine();
                    Continue = (response == "Yes");

                }
                catch (FormatException)
                {
                    Console.WriteLine("You entered a bad operation, try another one");
                }
            }

        }
    }
}

This is what I am getting and it is incorrect.

Ceasar Cipher

Type a string to encrypt:
Hello World

Shift: 1

Encrypted Data:
cfmmp rpsme

Decrypted Data:
bello qorld

Do you want to continue?
Type in Yes to continue or press any other key and then press enter to quit:
Broots Waymb
  • 4,713
  • 3
  • 28
  • 51
  • 2
    Well it's clearly an Encrypt problem. Have you stepped through with the debugger to see where it goes wrong? This is an excellent time to familiarize yourself with the VS debugger if not. – Broots Waymb Aug 02 '19 at 14:14
  • A-Z are actually different characters than a-z. See [The complete character list for UTF-16](http://www.fileformat.info/info/charset/UTF-16/list.htm) (Which is what C# uses internally) A-Z is 41 to 5A (hex) and a-z is 61 to 7A (hex). – Jesse de Wit Aug 02 '19 at 14:32

2 Answers2

3

The difficuty is that when encrypting uppercase letter, say 'Z' we can get lowercase one, e.g. 'd':

  'Z' + 10 == 'd'     

Let's extract upper and lower cases and use modulo arithmetics:

  for (int i = 0; i < alphabet.Length; i++) {
    char letter = alphabet[i];

    if (letter == ' ')
      continue;

    letter = (char)(char.IsLower(letter)
      ? (letter - 'a' + shift) % 26 + 26 + 'a'
      : (letter - 'A' + shift) % 26 + 26 + 'A');

    // Store.
    alphabet[i] = letter;
  }

Or extracting three cases:

  1. Lower case letters 'a'..'z'
  2. Upper case letters 'A'..'Z'
  3. Other characters (keep them as they are)

Code:

static string Encrypt(string value, int shift) {
  if (string.IsNullOrEmpty(value))
    return value; // Nothing to encrypt

  // ensure, that shift is in [0..25] range
  shift = ((shift % 26) + 26) % 26; 

  StringBuilder sb = new StringBuilder(value.Length);

  foreach(char letter in value)
    if (letter >= 'a' && letter <= 'z')
      sb.Append((char) ((letter - 'a' + shift) % 26 + 'a'));
    else if (letter >= 'A' && letter <= 'Z')
      sb.Append((char) ((letter - 'A' + shift) % 26 + 'A')); 
    else
      sb.Append(letter);

  return sb.ToString();
} 

// shift % 26 - in order to avoid integer overflow when shift == int.MinValue
static string Decrypt(string value, int shift) => 
  Encrypt(value, 26 - shift % 26);

Demo:

  string initial = "Hello World!";
  int shift = 10;

  string encrypted = Encrypt(initial, shift);
  string decrypted = Decrypt(encrypted, shift);

  Concole.Write($"{initial} -> {encrypted} -> {decrypted}");

Outcome:

  Hello World! -> Rovvy Gybvn! -> Hello World! 
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
2

Yeah your encyrption is broken. 'H' is not equal to 'h'. This line

else if (letter < 'a')
            {
                letter = (char)(letter + 26);
            }

Will always run for capital letters, since ALL capital letters are < 'a'. 'H' = 72, so the above code evaluates to letter = (char)(72 + 26), which shifted one to the right is 99 or 'c'.

Use char.IsUpper to split the logic. If char.IsUpper == true, then you need to handle this differently. Something like

if(letter.IsUpper()){
    if (letter < 'A') DoStuff
} else {
    if (letter < 'a') DoStuff
}
DetectivePikachu
  • 650
  • 3
  • 14