关于 CS50 pset2 vigenere
Posted
技术标签:
【中文标题】关于 CS50 pset2 vigenere【英文标题】:About CS50 pset2 vigenere 【发布时间】:2018-12-26 02:09:05 【问题描述】:为什么我的代码不会跳过空格并导致错误的加密顺序?
当我检查示例“Hello, World!”时,我的代码也计算了空间并转换为“Iekmo, Wnslc!”而不是“Iekmo,Vprke!”使用“baz”键
有人能解释一下背后的逻辑吗?非常感谢!
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
//getting user encryption key
int main(int argc, string argv[])
if (argc != 2)
printf("Usage: ./vigenere keyword\n");
return 1;
//check if all are alphabeticals
else
for (int i = 1; i < argc; i++)
for (int j = 0; j < strlen(argv[i]); j++)
if (isalpha(argv[i][j]) == false)
printf("Usage: ./vigenere keyword\n");
return 1;
//getting plaintext divide it into each character
string pt = get_string("plaintext: ");
printf("ciphertext: ");
//convert to ciphertext
//C = (P + k) % 26
for (int r = 0; r < strlen(pt); r++)
if (isupper(pt[r]))
//making loop with j group corresponding to keyword
int j = r % strlen(argv[1]);
int key = tolower(argv[1][j]) - 97;
printf("%c", (pt[r] - 65 + key) % 26 + 65);
else if (islower(pt[r]))
//making loop with j group corresponding to keyword
int j = r % strlen(argv[1]);
int key = tolower(argv[1][j]) - 97;
printf("%c", (pt[r] - 97 + key) % 26 + 97);
else
printf("%c", pt[r]);
printf("\n");
【问题讨论】:
您是否尝试过在调试器中单步执行代码以查看哪里出错了? 在stderr
(标准错误)流上报告错误是个好主意。您可以通过将for (int j = 0; j < strlen(argv[i]); j++)
替换为for (int j = 0; argv[i][j] != '\0'; j++)
来避免在每次迭代中调用strlen()
(并且有些人不会像那样完整地写出!= '\0'
)。但是,这些都不会直接影响代码的正确性。您应该使用'a'
和'A'
而不是97 和65;这使代码更具可读性和更容易理解。
您需要将“字符串r
中的位置”与“加密字符数”分开。您需要一个额外的变量,仅当字符为字母时才会增加。
旁注:对于您的第一个 if
,它执行 return
。因此,您可以消除上面的 else
并仅删除代码。也就是说,if (whatever) return; else do_stuff
转到 if (whatever) return; do_stuff
@Jonathan Leffler 感谢您的 cmets。但是我仍然无法弄清楚如何仅当它是字母时才增加我只能想到一个 for 循环来这样做,但它不适用于定义键并在 printf 中使用它。你能告诉我如何解决这个问题吗?
【参考方案1】:
问题在于int j = r % strlen(argv[1]);
的 j 计算。键的索引与r
(消息的索引)无关。程序需要仅根据密钥(长度)迭代密钥。每次“使用”键索引时都需要增加它,并“包装”它,这样它就不会超过结尾。您可以考虑在r
循环之前声明j
;每当您使用键索引时递增 j(提示:j++
);并使用模运算符“包装” j(提示:j % strlen(argv[1]
)。我把实际代码留给你。
【讨论】:
感谢您的建议。我现在已经想通了。感谢您的帮助!【参考方案2】:展示比解释容易得多:
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
if (argc != 2)
fprintf(stderr, "Usage: %s keyword\n", argv[0]);
return 1;
for (int i = 1; i < argc; i++)
for (int j = 0; argv[i][j] != '\0'; j++)
if (isalpha(argv[i][j]) == false)
fprintf(stderr, "%s: non-alphabetic character '%c' (%d) in key\n",
argv[0], argv[i][j], argv[i][j]);
return 1;
string pt = get_string("plaintext: ");
printf("ciphertext: ");
int k = 0;
int keylen = strlen(argv[1]);
for (int r = 0; pt[r] != '\0'; r++)
if (isupper(pt[r]))
int j = k++ % keylen;
int key = tolower(argv[1][j]) - 'a';
printf("%c", (pt[r] - 'A' + key) % 26 + 'A');
else if (islower(pt[r]))
int j = k++ % keylen;
int key = tolower(argv[1][j]) - 'a';
printf("%c", (pt[r] - 'a' + key) % 26 + 'a');
else
printf("%c", pt[r]);
printf("\n");
return 0;
示例运行:
$ ./vig89 baz
plaintext: Hello, World!
ciphertext: Iekmo, Vprke!
$
正如我在评论中指出的,您需要将“字符串中的位置,r
”与“加密字符数”分开。您需要一个额外的变量,仅当字符为字母时才会增加。
在上面的代码中,k
是额外的变量(keylen
是另一个变量,但它只是记录键的长度而不是重复调用strlen()
)。 k
中的值在知道字符是字母而不是其他情况时递增。
我观察到处理argv[1]
可能是明智的,这样您就不必每次都进行tolower()
转换;您可以在验证关键字时这样做。
我还报告了标准错误的错误,并且没有在循环的条件部分使用strlen()
。虽然如果您处理 3 个字母的键,成本并不高,但如果您开始计算每次迭代时 20 KiB 长的字符串的长度,您可能会开始发现开销(除非编译器设法将其优化掉——它可能,它可能不会)。我还在 I/O 中对齐了纯文本和密文。
还有很多其他可以/应该进行的更改。例如,第一个for (int i = 1; …)
循环是不需要的;您只有一个参数,因此您只需要内部 for (int j = 0; …)
循环。使用if (!isalpha(argv[i][j]))
也比将结果与false
比较更惯用,特别是因为isalpha
宏不能保证返回0 或1(它返回零或非零)——所以改变if (isalpha(argv[i][j]) == false)
到if (isalpha(argv[i][j] != true)
是不可靠的。我可能会创建一个简单的变量 char *key = argv[1];
(或在 CS50 的上下文中使用 string key = argv[1];
,尽管我不相信 CS50 typedef char *string;
是一个好主意)并在程序中使用它。
【讨论】:
非常感谢!!!!现在我懂了。这几天我一直在纠结背后的原因,非常感谢您的详细解释。以上是关于关于 CS50 pset2 vigenere的主要内容,如果未能解决你的问题,请参考以下文章
我的代码有啥问题? (Vigenere cypher cs50, pset2)