Search code examples
c++asciicaesar-cipher

Caesar cipher : how to calculate with shifting value > 10 ( or larger )?


As i know , the " formula " of Caesar Shifting is (x + k ) % 26 , where k is the shifting value and decryption just replace " + " to " - ".

but my code does not work when k > 10 (after i tested k = 10 , i find that the "shift" of the first few characters is wrong, so I estimate that k > 10 will be wrong (the number of incorrect characters increase) as well. ). I first change the characters to ASCII and then do the calculation. Finally change it back to characters.

Here are my code.

#include <iostream>
#include <string>
using namespace std;
int main() {
    string target;
    char s;
    int k, i, num, length, j;
    cin >> s >> k;
    getline(cin, target);

    for (j = 0; j <= (int)target.length(); j++) {
        if ((target[j]) = ' ') {
            target.erase(j, 1);
        }
    }

    length = (int)target.length();

    if (s == 'e') {
        for (num = 0; num <= length; num++) {

            if (isupper(target[num]))
                target[num] = tolower(char(int(target[num] + k - 65) % 26 + 65));
            else if (islower(target[num]))
                target[num] = toupper(char(int(target[num] + k - 97) % 26 + 97));
        }
    }
    else if (s == 'd') {
        for (num = 0; num <= length; num++) {
            if (isupper(target[num]))
                target[num] = tolower(char(int(target[num] - k - 65) % 26 + 65));
            else if (islower(target[num]))
                target[num] = toupper(char(int(target[num] - k - 97) % 26 + 97));
            }
        }
    cout << target;
    return 0;
}

Let me put down the case which i failed to run.

input:

d 10 n 3 V 3 D 3 N _ M Y N 3 _ S C _ N 3 L E ( input d / e first, then shifting value, finally the sequence of string require to " change ", the space is required to delete. )

the expected output:

D3l3t3d_cod3_is_d3bu

my output:

D3l3:3d_cod3_i9_d3b; Thanks!


Solution

  • Your issue is that when decoding you end up with negative numbers. With k == 13 the expression 'T' - k - 65 gives -7. -7 % 26 is still -7. -7+65 is 58 which isn't a letter.

    You can avoid negative numbers by simply setting k to 26 - k when decoding.

    Your code then simplifies to:

    if (s == 'd') {
        k = 26 - k;
    }
    for (num = 0; num <= length; num++) {
    
        if (isupper(target[num]))
            target[num] = tolower(char(int(target[num] + k - 'A') % 26 + 'A'));
        else if (islower(target[num]))
            target[num] = toupper(char(int(target[num] + k - 'a') % 26 + 'a'));
    }
    

    Note I've replaced your integer constants with their equivalent characters which makes the code much easier to understand. Note you also have a bug in your first loop (target[j]) = ' ' should be (target[j]) == ' '.

    Using all c++ has to offer you can reduce your code to:

    #include <iostream>
    #include <string>
    #include <algorithm>
    
    int main() {
        std::string target = "mXLM";
        char s = 'e';
        int k = 7;
    
        target.erase(std::remove(target.begin(), target.end(), ' '), target.end());
    
        if (s == 'd') {
            k = 26 - k;
        }
        std::string result;
        std::transform(target.begin(), target.end(), std::back_inserter(result), [k](char in) {
            if (isalpha(in)) {
                char inputOffset = isupper(in) ? 'A' : 'a';
                char outputOffset = isupper(in) ? 'a' : 'A';
                return char(int(in + k - inputOffset) % 26 + outputOffset);
            }
            return in;
        });
        std::cout << result;
        return 0;
    }