gianni85
gianni85

Reputation: 19

Caesar Cipher - Why while I'm iterating over a char array my program stop at a specific index?

I’m a novice in C and I’m practicing with some code in order to improve my knoowledge.

With the following code I’m trying to encrypt a given text returning, for every text character, a character that is the result of moving in the alphabet n positions forward.

So, for example, for the given text “hello” and the key 6 (so for every char of ‘hello‘ shifting of 6 positions in the alphabet chars array) the encrypted text will be “nkrru“.

My code use:

I also used two nested loop: in the first one I iterate, char by char, into the given text string and in the inner one I iterate into the alphabet chars in order to find the equivalent character. Then I populate the tmp array with the alphabet chars shifted by key times.

Meanwhile I manage the case if the shifting forward goes over the 26 letters of the alphabet and if one of the char is a white space.

At the end, using strcat() I concatenate tmp to strResult and print it.

#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main()
{
    char ALPHABET[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
    int key = 9;
    string word = "Hello World";//text to encrypt

    char resultStr[strlen(word)];//string where to store the resulting encrypted text
    char tmp[strlen(word)];//temporary string

    int counter;//for the shifted index

        for (int i = 0, n = strlen(word); i < n; i++)
        {

            char wordcurrChar = tolower(word[i]);//convert every wordcurrChar to lowercase
            int checkSpace = isspace(wordcurrChar);//check if the character is a white space

            //inner loop to iterate trough alphabet array
            for (int j = 0, m = strlen(ALPHABET); j < m; j++)
            {
                counter = j+key;//counter has now the value of the shifted index
                char ALPHcurrChar = ALPHABET[j];

                    if ((wordcurrChar == ALPHcurrChar) && (counter <= 26)) {//if counter is whitin the 26 characters of the alphabet
                        tmp[i] = ALPHABET[counter];//then shift at index counter
                    } else if ((wordcurrChar == ALPHcurrChar) && (counter > 26)) {//if counter is over the 26 characters of the alphabet
                        int newCounter = counter - 26;//then, once over Z, start from the beginning of the alphabet
                        tmp[i] = ALPHABET[newCounter];
                    } else if (checkSpace != 0) {//if the character is a white space
                        tmp[i] = ' ';//then return a white space also for the encrypted text
                    }
            }

        }
        strcat(resultStr, tmp);//concat resultStr and temporary string
        printf("%s\n", resultStr);//print the encrypted text
}

Everithing looks to work fine, except from the fact that when the shifting goes over the 26 alphabet chars and has to return as encrypted char the specific letter 'A' it stops.

So, for example, if I give as string to encrypt hello world with key=9, result is qnuux fx_ _ _. The last 3 letters are missing. Basically, the shifting of char r in hello world (that shifted of 9 position in the alphabet array goes over the 26th position and end in in char a position) stops everything.

shifting schema 2

Again, this happen only if the shifting goes over the 26 letters of the alphabet and end on the specific position of char a (with all other cases works fine). So with hello world and key = 15, the result will be wt_ _ _ _ _ _ _ _ stopping at the shifting of the first l of hello world. This because the shifting goes over the 26 alphabet letters and reach char a position.

shifting schema 3

I spent hours and hours to understand why is happening but with no success.

Hope someone can help.

Thanks a lot in advance.

P.s: I tried many different changing of the if statment that manage the condition of going over the 26 alphabet array positions but nothing really helped.

Upvotes: 1

Views: 442

Answers (2)

जलजनक
जलजनक

Reputation: 3071

  1. You can simply initialise a char array with a string.
char ALPHABET[] = "abcdefghijklmnopqrstuvwxyz";
  1. It's better to #define magic numbers
#define CIPHER_ROTATIONS   9
  1. Every cipher should be recoverable, for data consistency. So, ignoring letter-case is loss of info.
char wordcurrChar = tolower(word[i]);

Simplified code:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define CIPHER_ROTATIONS   27

// shift can be positive(encoding) or negative(decoding)
void caeser_cipher (const char* input, const int tlen, int shift, char* output)
{
    shift %= 26;                // more than 26 make cycle[s]
    if (shift < 0) shift += 26; // moving x steps backward = moving (26-x) forward
    for (int ti=0; ti < tlen; ++ti) {
        if (islower (input[ti]))
            output[ti] = 'a' + (input[ti] - 'a' + shift) % 26;
        else if (isupper (input[ti]))
            output[ti] = 'A' + (input[ti] - 'A' + shift) % 26;
        else
            output[ti] = input[ti];
    }
}


int main() {
    char text[] = "Caesar cipher, also known as Caesar's cipher, the shift cipher, Caesar's code or Caesar shift";
    int tlen = strlen(text);
    char cipher[tlen +1]; cipher[tlen] ='\0';
    int shift = CIPHER_ROTATIONS; // also called rotations

    printf ("Text :\t\t[%s]\n", text);
    caeser_cipher (text, tlen, shift, cipher);
    printf ("Cipher:\t\t[%s]\n", cipher);

    text[0] = '\0';
    caeser_cipher(cipher, tlen, -shift, text);
    printf ("DecipheredText: [%s]\n", text);

    return 0;
}

Upvotes: 1

chux
chux

Reputation: 153557

Code seems to have missed the idea that with the C library, a string has a '\0'

A string is a contiguous sequence of characters terminated by and including the first null character. C17dr 7.1.1 1


At least these problems:

strlen() on a non-string

strlen(ALPHABET) leads to undefined behavior (UB) as strlen() expects a string and ALPHABET is not a string as it lacks a terminating null character.

// Bad
char ALPHABET[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
        for (int j = 0, m = strlen(ALPHABET); j < m; j++)

// Add null character
char ALPHABET[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '\0'};
// or simply
char ALPHABET[] = "abcde...wxyz";;

strcat() with a non-string

Similarly, strcat() below fails as strcat() expects resultStr and tmp to be strings. Neither are strings as they lack a terminating null character.

char resultStr[strlen(word)];
char tmp[strlen(word)];
....
// Bad
strcat(resultStr, tmp);

Alternate code would increase array size by 1 and add the final '\0'.

Not-productive to repeated walk the word to find its length.

size_t word_length = strlen(word);
char resultStr[word_length  + 1u];
char resultStr[0] = '\0';
char tmp[word_length + 1u];
  ....
char tmp[word_length] = '\0';
strcat(resultStr, tmp);

Upvotes: 2

Related Questions