想做凯撒密码但无法更改最后两个字符
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 = 1
,z
环绕到 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' 的代码以获得字符 a
到 z
. 的值 0 到 25
... + key
- 添加密钥 "shift"
... % ('z' - 'a' + 1)
- 这个常量表达式在实践中解析为 % 26
,解析为 "wrap-around"。
... +
a- transform the range 0 to 25 back into character codes
ato
z`.
我为凯撒密码编写了一个代码并且该代码有效,除了我不能加密超过 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 = 1
,z
环绕到 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' 的代码以获得字符a
到z
. 的值 0 到 25
... + key
- 添加密钥 "shift"... % ('z' - 'a' + 1)
- 这个常量表达式在实践中解析为% 26
,解析为 "wrap-around"。... +
a- transform the range 0 to 25 back into character codes
ato
z`.