所以我正在处理一个SC50问题,我需要制作一个简单的密码,并能够加密单词或句子。。。整整两天后,我终于想明白了,但我的代码真的很长,经过谷歌搜索,我发现了一个更好的版本。这真的很容易,除了有一部分我不太了解它是如何工作的,我真的很想知道它是如何。。。下面是完整的代码(不幸的是,我现在似乎找不到代码的原始来源,但实际上我自己至少做了一半,只复制了"//替换"之后的部分(:
还有,我想知道的是,这两行:
printf("%c", toupper(arg[plaintext[i] - 65])); //calculation to print the encipher text amd make sure it is Uppercase (case doesn't change)
和
printf("%c", tolower(argv[1][plaintext[i] - 97])); ///calculation to print the encipher text amd make sure it is lowercase(case doesn't change)
我绕不开我的头,怎么计算"-65〃;以及"-66〃;正在解决问题。。。假设在我的密钥中,第一个字母是Q,当我写和a时,它应该代替Q…
Ascii表上的A=65和Q=81,所以当我取65-65…mm时,我为什么要这样做?很明显,这个程序需要这样做才能正常工作,但我不明白它是如何工作的,以及实际发生了什么。。。
这些计算背后的逻辑是什么?请帮忙!
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[]) {
if (argc != 2) {
printf("Usage: ./substitution keyn");
return 1;
}
string arg = argv[1];
int chars = 0;
for (int i = 0; i < strlen(arg); i++) {
if (isalpha(arg[i])) {
for (int j = i+1; j < strlen(arg); j++) {
if (toupper(arg[j]) == toupper(arg[i])) {
printf("Key must not contain repeated alphabets.n");
return 1;
}
}
chars += 1;
}
}
if (chars != 26) {
printf("Key must contain 26 characters.n");
return 1;
}
// SUBSTITUTION
printf("%sn", arg);
string plaintext = get_string("plaintext: "); //Getting user's input as plaintext
printf("ciphertext: "); //to print the ciphertext
int plaintext_length = strlen(plaintext); //get the strlen of plaintext (user's input)
for (int i = 0; i < plaintext_length; i++) { //iterate over the plaintext_Length
if (isupper(plaintext[i])) { //check if plaintext character is uppercase
printf("%c", toupper(arg[plaintext[i] - 65])); //calculation to print the encipher text amd make sure it is Uppercase (case doesn't change)
}
else if (islower(plaintext[i])) { //check if plaintext character is lowercase
printf("%c", tolower(arg[plaintext[i] - 97])); ///calculation to print the encipher text amd make sure it is lowercase(case doesn't change)
}
else { //if plaintext is anything else, print it like that without changing
printf("%c", plaintext[i]);
}
}
printf("n"); //print new line
}
一般来说,这是一个错误的代码。
例如,代替使用幻数65或97
printf("%c", toupper(arg[plaintext[i] - 65]));
printf("%c", tolower(argv[1][plaintext[i] - 97]));
最好写
printf("%c", toupper(arg[plaintext[i] - 'A']));
printf("%c", tolower(argv[1][plaintext[i] - 'a']));
argv[1]或arg包含26个字母的字符串,例如
"xyzabcgtidefuvwjklmn0rspqh"
如果你有一个字符串,例如";你好"则"H"-"A"给出值7。使用您可以在字符串argv[1]
或字母t 所指向的数组中的位置7找到的数字
"xyzabcgtidefuvwjklmn0rspqh"
^
|
'H'
因此,源字符串中的字母'H'
被编码为类似于字母'T'
。对于第二个字母"e",您有"e"-"a"等于4
。所以你有
"xyzabcgtidefuvwjklmn0rspqh"
^
|
'e'
所以字符串"的前两个字母Hello";变成">Tbllo";。这种方法用于对源字符串的剩余字母进行加密
"。。。我无法理解">
是的,很难看出代码中发生了什么,从而避免(适当地(使用感兴趣数据的临时副本。
没有评论,这就是";令人困惑的";部分代码,重写为临时使用单个字符变量:
// SUBSTITUTION
printf("%sn", arg);
string plaintext = get_string("plaintext: "); //Getting user's input as plaintext
printf("ciphertext: "); //to print the ciphertext
int plaintext_length = strlen(plaintext); //get the strlen of plaintext (user's input)
for (int i = 0; i < plaintext_length; i++) //iterate over the plaintext_Length
{
char c = plaintext[i];
if( isupper( c ) )
{
printf("%c", toupper(arg[c - 65]));
}
else if ( islower( c ) )
{
printf("%c", tolower(arg[c - 97]));
}
else
{
printf("%c", plaintext[i]);
}
}
现在很明显会有一个输出字符,所以对printf((的3次调用会分散注意力。简化后会导致"重新使用"临时char变量。(此处仅显示for((循环(:
for (int i = 0; i < plaintext_length; i++) //iterate over the plaintext_Length
{
char c = plaintext[i];
if( isupper( c ) )
{
c = toupper(arg[c - 65]);
}
else if ( islower( c ) )
{
c = tolower(arg[c - 97]);
}
else
{
c = plaintext[i];
}
printf( "%c", c );
}
现在很明显,最后的"其他"是多余的:
for (int i = 0; i < plaintext_length; i++) //iterate over the plaintext_Length
{
char c = plaintext[i];
if( isupper( c ) )
{
c = toupper(arg[c - 65]);
}
else if ( islower( c ) )
{
c = tolower(arg[c - 97]);
}
printf( "%c", c );
}
值"65"one_answers"97"被称为"0";幻数";(您已经理解的分别对应于ASCII"A"one_answers"A"。(清理这种不良做法。
if( isupper( c ) )
{
c = toupper( arg[ c - 'A' ] );
}
else if ( islower( c ) )
{
c = tolower( arg[ c - 'a' ] );
}
printf( "%c", c );
现在很明显,每个输入字符的"大小写"决定了相应输出字符的"大写"。
现在也应该很明显,正在计算明文字符("A/A"=0,"B/B"=1,"C/C"=2(的"A"或"A"的差异"偏移量"。该计算的结果成为26个字符的加密密钥的INDEX。你的"Q"变成了"16",所以键的第16个字符是"Q";抬头看";,转化为适当的案例,然后使用。
此操作可以根据以下内容进一步减少:
for (int i = 0; i < plaintext_length; i++) //iterate over the plaintext_Length
{
char c = plaintext[i]; // copy of plaintext character
if( isalpha( c ) ) { // translate only alphabetic chars
c = tolower( c ) - 'a'; // 'a-z' ==> '0-25'
c = arg[ c ]; // use as index into key.
if( isupper( plaintext[i] ) // make case of enciphered char match input
c = toupper( c );;
}
printf( "%c", c );
}
或者,甚至更多:
for (int i = 0; i < plaintext_length; i++) //iterate over the plaintext_Length
{
char c = plaintext[i]; // copy of plaintext character
if( isalpha( c ) ) { // translate only alphabetic chars
c = arg[ tolower( c ) - 'a' ]; // select corresponding 'key' character
if( isupper( plaintext[i] ) // make case of enciphered char match input
c = toupper( c );;
}
printf( "%c", c );
}
尽管这看起来很复杂,但它的简洁就是它的力量。
EDIT:isalpha()
toupper()
和tolower()
是标准的C函数。代码需要:#include <ctype.h>
才能使用这些函数。
EDIT2:toupper()
和tolower()
将返回一个unsigned char
。要在没有警告的情况下编译,请更改"c"的声明:
unsigned char c = plaintext[i]; // copy of plaintext character
第3版:您的OP没有询问";验证码";你说你写的。对不起,这还不够。当它确认键中有26个不同的字符时,用户可以键入一个包含附加标点符号的键;abc";包含3个不同的字母;a.b:!c";。。。您测试isalpha()
。如果在密钥中发现非alpha字符,为什么不立即停止?正如所写的,可能会使用非法密钥,并且"加密"非常非常不正确。。。