想做凯撒的密码,但不能更改最后两个字符

时间:2019-08-26 10:18:15

标签: c caesar-cipher

我为caesar的密码编写了一个代码,该代码可以正常工作,除了我不能对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;
        }
    }
}

1 个答案:

答案 0 :(得分:2)

caesar()似乎只是以非常复杂的方式简单地将字符b的位置返回到数组中的z,并且完全省略了!而且,由于alphabets不是以null结尾的字符串,strlen()在任何情况下都不是有效的操作。 alphabets[j+key]中的main()(错误地)进行了“加密”,使得caesar()的命名特别差-因为它根本不这样做。

以下函数将返回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被“知道”的地方进行加密更有意义。像以前那样在caesar()main()之间划分密码步长是一个糟糕的设计,缺乏内聚力和不必要的 coupling

如果字符x出现在alphabet中,则会被alphabet[(i + key) % sizeof( alphabet )] ;修改。这会像往常一样添加key,而且还会“环绕”(%模运算),例如对于key = 1z会绕到{ {1}}而不是像代码一样引用a数组末尾的字节。至关重要的是,如果它未出现在alphabet中,则它没有被修改-这就是alphabetcipher初始化的原因。修改xcipher)或到达cipher != x的结尾时,循环退出。

然后在alphabet的迭代中:

plain_text

这里的for (i = 0; i <= strlen(plain_text); i++ ) { cipher_text[i] = caesar( plain_text[i], key ) ; } 很特殊,但是这里可以确保将nul终止符复制到<= strlen()-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 之后cipher = x,而不是在for循环中测试break-减少测试次数-但是arguably breaks structured programming “规则”-我不会批评别人使用它,但这不是我的偏好。在这种情况下,您可能还使用cipher来完全跳过循环,但是例如,该实现具有针对重音字符的实现定义的行为,因此,如果要扩展受支持的“字母”,它可能无法按预期工作。

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

isalpha(x)

严格地,这假设字符a到z在目标字符集中是连续的,但是对于可能在其上运行该代码的任何系统(即不是IBM 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 ; } 阵列解决方案仍然有效。我指出这一点仅仅是因为否则有人会对此发表评论,好像这确实是一个问题。

要解释该表达式:alphabet

  • cipher = ((cipher - 'a') + key) % ('z' - 'a' + 1) + 'a'-减去'a'的代码,将字符(cipher - 'a')a的值从0到25。
  • z-添加键“ shift”
  • ... + key-实际上,此常量表达式解析为... % ('z' - 'a' + 1),称为“环绕”。
  • % 26 a ... + a - transform the range 0 to 25 back into character codes z`。