想做凯撒密码但无法更改最后两个字符

Wanted to do caesar's cipher but couldn't change the last two characters

我为凯撒密码编写了一个代码并且该代码有效,除了我不能加密超过 8 个字母而且我也不能处理空格。它显示“>>”这个符号而不是空格。另外,我想在我的代码的第二个函数中进行二进制搜索,但我不知道我是否这样做了。

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

char caesar (char x, char alphabets[]);

int j;

int main()

{

    char* plain_text = malloc (10 * sizeof(char)  + 1);
    int key, num;
    char* cipher_text = malloc (10 * sizeof(char)  + 1);

    printf("Plain text: ");
    gets(plain_text);
    printf("\nThe plain text is:  ");
    puts(plain_text);
    printf("\nKey: ");
    scanf("%d", &key);
    num = (int)key;

    if (key != num)
    {
        return 1;
    }
    int i;
    char alphabets[] = {'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 (i=0;i<=strlen(plain_text);i++)
    {
        char x = plain_text[i];
        caesar(x, alphabets);
        cipher_text[i] = alphabets[j+key];
        printf("%c", cipher_text[i]);
    }
    free(plain_text);
}

char caesar (char x, char alphabets[])

{

    if(x == alphabets[13])
    {
        return 13;
    }
    for(j = 1; j <= 13; j++)
    {
        if(x == alphabets[j])
        {
            return j;
        }
    }
    for(j = 13; j <= strlen (alphabets); j++)
    {
        if(x == alphabets[j])
        {
            return j;
        }
    }
}

caesar() 似乎简单地 return 字符 b 到 z 在数组中的位置以一种非常复杂的方式并且完全省略了 a!此外,由于 alphabets 不是空终止字符串 strlen() 在任何情况下都不是有效操作。 "encryption" 由 alphabets[j+key]main() 中(错误地)完成,使得 caesar() 的命名特别糟糕 - 因为这根本不是它所做的。

以下函数将return alphabet 中任何字符的密码,并保持任何其他字符不变:

char caesar( char x, int key )
{
    const 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'};

    char cipher = x ;

    for( int i = 0; 
         cipher == x && i < sizeof( alphabet ); 
         i++ )
    {
        if( alphabet[i] == x )
        {
            cipher = alphabet[(i + key) % sizeof( alphabet )] ;
        }
    }

    return cipher ;
}

key 传递给 ceasar() 比传递常量 alphabet 和进行加密更有意义 alphabet 是 [=114] =].像你所做的那样在 caesar()main() 之间拆分密码步骤是一个糟糕的设计,缺乏 凝聚力 并且有不必要的 耦合 .

如果字符 x 出现在 alphabet 中,它被 alphabet[(i + key) % sizeof( alphabet )] ; 修改。这会像您一样添加 key,但也会添加 "wraps-around"(% 模运算),因此例如对于 key = 1z 环绕到 a 而不是像您的代码那样引用 alphabet 数组末尾之外的一个字节。重要的是,如果它没有出现在 alphabet 中,它就是未修改的——这就是 cipher 被初始化为 x 的原因。当 cipher 被修改(cipher != x),或者到达 alphabet 的末尾时,循环退出。

然后在plain_text的迭代中:

for (i = 0; i <= strlen(plain_text); i++ )
{
    cipher_text[i] = caesar( plain_text[i], key ) ;
}

这里的<= strlen()是不寻常的,但是这里确保nul终止符被复制到cipher_text——它不会被caesar()修改。

请注意,上述解决方案仅加密小写文本(与您的原始代码一样)。您的代码中还有其他问题和不明智的做法,在评论中进行了讨论,但可能与您的问题没有直接关系,但使用上述功能,以下完整实现解决了大部分问题:

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

char caesar( char x, int key ) ;

#define MAX_TEXT 128

int main()
{
    char plain_text[MAX_TEXT] = "" ;
    char cipher_text[MAX_TEXT] = "" ;

    printf( "Plain text: " );
    fgets( plain_text, MAX_TEXT, stdin ) ;

    printf( "\nThe plain text is: %s\n", plain_text ) ;

    printf( "Key: " ) ;
    int key = 0 ;
    scanf( "%d", &key );

    for( size_t i = 0; i <= strlen( plain_text ); i++ )
    {
        cipher_text[i] = caesar( plain_text[i], key ) ;
    }

    printf( "\nThe cipher text is: %s\n", cipher_text ) ;
    return 0 ;
}

示例:

Plain text: abc, xyz

The plain text is: abc, xyz

Key: 1

The cipher text is: bcd, yza

允许大写字母的修改:

#include <ctype.h>

char caesar( char x, int key )
{
    const 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'};

    char cipher = x  ;

    for( int i = 0;
         cipher == x && i < sizeof( alphabet );
         i++ )
    {
        if( alphabet[i] == tolower( x ) )
        {
            cipher = alphabet[(i + key) % sizeof( alphabet )] ;
            if( isupper( x ) )
            {
                cipher = toupper( cipher ) ;
            }
        }
    }

    return cipher ;
}

此处测试 alphabet[i] == tolower( x ) 忽略大小写,然后当找到匹配时应用 cipher = toupper( cipher ) 如果 x 是大写,则产生大写密码。

示例输出:

Plain text: aBc, XyZ 123

The plain text is: aBc, XyZ 123

Key: 1

The cipher text is: bCd, YzA 123

请注意,不是在 for 循环中测试 cipher = x,您可以在循环中分配 cipher 之后 break - 减少测试次数 - 但是 arguably breaks structured programming "rules" - 我不会批评其他人使用它,但这不是我的偏好。在这种情况下,您也可以使用 isalpha(x) 来完全跳过循环,但是它具有重音字符的实现定义行为,因此如果您要扩展支持的 "alphabet",它可能无法正常工作故意的。

如果您只使用字母表中的字符 a 到 z,则可以进一步简化,其中可以使用字符代码值通过算术方式确定密码:

char caesar( char x, int key )
{
    char cipher = tolower( x ) ;

    if( isalpha( x ) )
    {
        cipher = ((cipher - 'a') + key) % ('z' - 'a' + 1) + 'a' ;
        if( isupper( x ) )
        {
            cipher = toupper( cipher ) ;
        }
    }

    return cipher ;
}

严格来说,这假定字符 a 到 z 在目标字符集中是连续的,但这对于您可能 运行 此代码的任何系统(即不是 IBM Z 系列大型机)都是普遍适用的或各种古董 mainframe/mini 计算机),如果不是,则 alphabet 数组解决方案仍然有效。我指出这一点只是因为否则有人会发表评论,就好像这真的是一个问题。

解释表达式:cipher = ((cipher - 'a') + key) % ('z' - 'a' + 1) + 'a' :

  • (cipher - 'a') - 减去 'a' 的代码以获得字符 az.
  • 的值 0 到 25
  • ... + key - 添加密钥 "shift"
  • ... % ('z' - 'a' + 1) - 这个常量表达式在实践中解析为 % 26,解析为 "wrap-around"。
  • ... +a- transform the range 0 to 25 back into character codesatoz`.