循环 ASCII 字符并解密消息

Looping ASCII characters and decrypting the message

老师给我布置了作业:-

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.

这是我的代码:-

#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;
}

现在我所应用的逻辑肯定是错误的。我所有的测试用例都失败了。我怀疑问题出在以下代码片段中:-

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'.
        }
    }

这个简单的问题我做不出来。请帮忙。

部分测试用例如下:-

#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] = '[=10=]'; // Removing the # at the end

    puts(array);

    return 0;
}

第一,您不是以 null 终止您的字符串,因此有时会在末尾出现额外的字符。我通过要求一个字符串而不是连续的几个字符来修复它,我个人觉得这更简单。然后它会自动在您的数组末尾添加一个 [=11=] ,但它不会逐个字符地添加。话虽这么说,你的也可以工作,但前提是你在末尾添加一个 array[counter] = '[=12=]' 以 null 终止它。

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

您的实施过于复杂。这里只需要一行 array[i] = ((array[i] - 'a' + offset) % 26) + 'a';

为了分解它,我们通过 array[i] - 'a' 得到字母在字母表中的索引;我们添加偏移量,我们 运行 模 26(字母表中的字符数)以获得新字母的索引并说明我们可以获得高于 26 的结果(在这种情况下我们想换行:29 表示字母表中的第三个字母,因为 29 - 26 = 3)。我们加回 'a' 以获得实际字符而不是索引,我们得到了正确的字母。

此外,您的代码目前容易受到缓冲区溢出的影响!您不应该太在意学校项目,但这是一个巨大的安全漏洞,您可能有兴趣了解更多信息。如果您的输入字符串中没有 #,您也很容易出现分段错误。

我有意没有对您的代码进行太多改进,因为它是一个学校项目,但我知道变量 counter 完全没有必要,您可以重新设计代码以避免使用它。您可能需要考虑如何解决这个问题。

以下是按所需偏移量简单地内联替换字符。我没有优化替换语句,因为我觉得这样更易读。

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

int main()
{
    char buf[101]; // one character extra for max input case
    char *p; // iteration pointer
    int d; // for "delta"

    scanf("%s", buf);

    d = *buf - 't'; // record the "rot13" encryption offset
    for (p = buf; *p != '#'; p++)
        *p = (((*p - 'a') + 26 - d) % 26) + 'a';
    *p = '[=10=]';

    printf("%s\n", buf);

    return 0;
}

记住:这是 C,始终注意无效输入。 通过一些基本的错误检查:

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

int main()
{
    char buf[101]; // one character extra for max input case
    char *p; // iteration pointer
    int d; // for "delta"

    switch (scanf("%100[a-z#]", buf)) {
    case EOF:
        fprintf(stderr, "empty file\n");
        return 1;
    case 0:
        fprintf(stderr, "invalid input\n");
        return 1;
    }

    d = *buf - 't'; // record the "rot13" encryption offset
    for (p = buf; *p != '#' && *p != '[=11=]'; p++) {
        *p = (((*p - 'a') + 26 - d) % 26) + 'a';
    }

    if (*p != '#') {
        fprintf(stderr, "missing termination character\n");
        return 1; // omit this line if you want best-effort output anyway
    }

    *p = '[=11=]';

    printf("%s\n", buf);

    return 0;
}

这应该处理太短或太长的输入、包含无效字符、缺少 # 字符或只是一个空文件。