Warm tip: This article is reproduced from serverfault.com, please click

Looping ASCII characters and decrypting the message

发布于 2020-12-05 07:57:08

My teacher gave me a homework: -

You are given a string (of at most 100 characters) consisting of lowercase characters, terminated with a # symbol. The string is the "encrypted" form of an original string as follows - each character in the original string has been shifted by a fixed integer n (where 1<= n <= 25).

Assuming that a is the 0th character in the alphabet, ..., and z is the 25th character of the alphabet, in the encrypted version, we have:

'a' becomes the nth character in the alphabet, 'b' becomes the (n+1)%26th character in the alphabet, ...

and so on.

n is not known to you. You only know that the first character represents 't'. From this information, you have to output the original string.

Here is my code for it: -

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char array[100]; //string will be stored here.
    char a;
    int i = 0; //initializer
    
    scanf("%c", &a);
    
    int counter = 0; // to count the size of the string
    
    while(a != '#')
    {
        array[i] = a;
        i++;
        counter++;
        scanf("%c", &a);
    }
    
    a = 't';
    
    int temp = a - array[0]; //To get the offset of the encrypted message.
    
    for(i = 0; i < counter; i++)
    {
        if((array[i] + temp) >= 'a' && (array[i] + temp) <= 'z')
        {
            array[i] = array[i] + temp; //Applying the offset to the encrypted message to get the true message.
        }
        
        else if((array[i] + temp) < 'a')
        {
            array[i] = 'z' - (temp - (array[i] - 'a')); //If offset is large and it falls below ASCII charater number 'a' then loop back to 'z'.
        }
        
        else
        {
            array[i] = 'a' + (temp - ('z' - array[i])); //If offset is large and it falls above ASCII charater number 'z' then loop back to 'a'.
        }
    }
    
    puts(array);
    
    return 0;
}

Now the logic which I have applied is certainly wrong. All of my test cases are failing. I suspect the problem is in the following snippet of the code: -

for(i = 0; i < counter; i++)
    {
        if((array[i] + temp) >= 'a' && (array[i] + temp) <= 'z')
        {
            array[i] = array[i] + temp; //Applying the offset to the encrypted message to get the true message.
        }
        
        else if((array[i] + temp) < 'a')
        {
            array[i] = 'z' - (temp - (array[i] - 'a')); //If offset is large and it falls below ASCII charater number 'a' then loop back to 'z'.
        }
        
        else
        {
            array[i] = 'a' + (temp - ('z' - array[i])); //If offset is large and it falls above ASCII charater number 'z' then loop back to 'a'.
        }
    }

I am not able to do this easy question. Please help.

Some of the test cases are mentioned below: -

Test cases

Questioner
Swarnim Khosla
Viewed
0
OctaveL 2020-12-05 20:41:44
#include <stdio.h>
#include <stdlib.h>

int main()
{
    char array[100]; //string will be stored here.
    int counter = 0; // to count the size of the string

    scanf("%s", array);

    while(array[counter] != '#')
        counter++;

    int offset = 't' - array[0]; //To get the offset of the encrypted message.

    if (offset < 0)
        offset = 26 + offset;

    for(int i = 0; i < counter; i++)
        array[i] = ((array[i] - 'a' + offset) % 26) + 'a';

    array[counter] = '\0'; // Removing the # at the end

    puts(array);

    return 0;
}

One, you weren't null terminating your string, hence the extra characters at the end at times. I fixed it by asking for a string instead of several characters in a row, which I find a lot simpler personally. It then adds a \0 at the end of your array automatically, while it doesn't with your character by character approach. That being said, yours can also work, but only if you add a array[counter] = '\0' at the end to null terminate it.

'a' becomes the nth character in the alphabet, 'b' becomes the (n+1)%26th character in the alphabet, ...

Your implementation was vastly overcomplicated. A single line with array[i] = ((array[i] - 'a' + offset) % 26) + 'a'; is all that's required here.

To break it down, we get the index of the letter in the alphabet by doing array[i] - 'a' ; we add the offset, we run a modulo 26 (the number of characters in the alphabet) to get the index of the new letter and to account for the fact we can get a result higher than 26 (in which case we want to wrap around : a 29 would mean the third letter of the alphabet, because 29 - 26 = 3). We add back 'a' to get an actual character instead of an index, and we've got the correct letter.

Also, your code is currently susceptible to buffer overflows! You shouldn't care too much for a school project, but it's a gaping security flaw you may be interested in learning more about. You're also highly susceptible to a segmentation fault if there's no # in your input string.

I've purposefully not improved upon your code too much as it is a school project, but know the variable counter is wholly unnecessary, and you can redesign your code to avoid having to use it. You may want to think about how to go about this.