我实现了一个Vigenere密码,它保留大小写,如果传递-d参数,也可以解密。使用是./vigenere <plaintext|ciphertext> key [-d],它工作得很好,但我认为可能有一些事情我可以做得更好。所以,我只是在找批评。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define ENCRYPT 1
#define DECRYPT -1
int mod(int x, int n)
{
return (x % n + n) % n;
}
char *vigenere(char *input, char *keystream, int mode)
{
if (strlen(keystream) != strlen(input))
return NULL;
int output_len = strlen(input), offset = 'a', wasupper = 0;
char *output = malloc(output_len);
for (int i = 0; i < output_len; ++i)
{
if (isupper(input[i]))
{
input[i] = tolower(input[i]);
wasupper = 1;
}
else
wasupper = 0;
output[i] = mod((input[i] - offset) + (keystream[i] - offset) * mode, 26) + offset;
if (wasupper)
output[i] = toupper(output[i]);
}
return output;
}
char *gen_keystream(char *key, int keystream_len)
{
int key_len = strlen(key), j = 0;
char *keystream = malloc(keystream_len);
for (int i = 0; i < keystream_len; j = ++i % key_len)
keystream[i] = key[j];
return keystream;
}
int main(int argc, char **argv)
{
if (argc < 3)
return EXIT_FAILURE;
int mode = ENCRYPT;
if (argc > 3 && strcmp(argv[3], "-d") == 0)
mode = DECRYPT;
int keystream_len = strlen(argv[1]);
char *keystream = gen_keystream(argv[2], keystream_len);
char *result = vigenere(argv[1], keystream, mode);
printf("%s\n", result);
free(keystream);
free(result);
return EXIT_SUCCESS;
}发布于 2020-11-23 19:18:13
。
程序的工作方式是将输入作为命令行参数传递,但是如果要对整个文件进行编码或解码怎么办?如果输入很大怎么办?我将尝试修改程序,以便它可以从文件中运行,或者在没有指定文件的情况下从标准输入运行,因此可以这样做:
./vigenere key < plaintext.txt > ciphertext.txt发布于 2020-11-23 19:55:11
以下是一些可以帮助您改进代码的东西。
目前的守则包括:
if (isupper(input[i]))
{
input[i] = tolower(input[i]);
wasupper = 1;
}
else
wasupper = 0;
output[i] = mod((input[i] - offset) + (keystream[i] - offset) * mode, 26) + offset;
if (wasupper)
output[i] = toupper(output[i]);问题是,else后面的缩进表明,只有当isupper(input[i])计算为false时才执行所有这些行,但情况并非如此。只有wasupper = 0;行在该路径上被独占执行。因此,代码的读者必须试图找出它是缩进错误还是缺少大括号错误。出于这个原因,特别是如果您对该语言还不熟悉,我建议对此类条件结构始终使用{}。
C中的字符串必须以'\0'字符终止,才能使用函数调用(如strlen() ),但gen_keystream和vigenere中存在错误,无法解释终止符。请记住,strlen()返回不包括此终止字符的字符串的长度。
这个函数并没有中断,但是它并没有达到它所能达到的效率:
int mod(int x, int n)
{
return (x % n + n) % n;
}我们可以使用简单的return (x + n) % n;来表达相同的概念,考虑到x的预期范围是(-n,n)。您还可以简化一点以上的内容,我将在后面演示。
中使用const
因为vigenere不应该改变input或keystream,所以两者都应该声明为const。
上
这不利于代码的可读性,在同一行中声明多个变量,或者滥用逗号运算符:
int output_len = strlen(input), offset = 'a', wasupper = 0; 相反,在自己的行中分离每个语句可以使代码更容易阅读和维护。请参阅ES.10
对malloc的调用可能失败。您必须检查返回值,以确保它们没有,或者当给定错误的输入或由于系统资源不足时,您的程序可能崩溃(或更糟)。严格的错误处理是大多数工作软件和无错误软件之间的区别。你应该为后者而奋斗。
函数
还有其他方法来构造程序,这样就不需要gen_keystream或keystream了。您可以简单地将密钥传递到修改过的vigenere版本中。
对每个C程序员来说,有效地使用指针是一项基本技能。下面是您的函数的重写版本,使用了上面提到的大多数简化和更正:
char *vigenere(const char *input, const char *key, int mode)
{
int output_len = strlen(input);
if (output_len == 0) {
return NULL;
}
// add +1 for the terminating NUL character
char *output = malloc(output_len + 1);
if (output == NULL) {
return output;
}
const char *keyptr = key;
output[output_len] = '\0';
for (char *curr = output ; *input; ++input) {
if (*keyptr == '\0') {
keyptr = key;
}
*curr = (mode * (*keyptr - tolower(*input)) + 26) % 26 + 'a';
if (isupper(*input)) {
*curr = toupper(*curr);
}
++curr;
++keyptr;
}
return output;
}注意,在这段代码中,我假设已经检查了所有字符,对于所有输入,isalpha()都是真的,对于键中的每个字符,islower()和isalpha()都是真的。
发布于 2020-11-24 17:10:51
这一行:
if (strlen(keystream) != strlen(输入))返回NULL;
意味着键需要与输入完全相同的长度。所以你真正拥有的是一个一次性的垫子。
相反,一个真正的Vignère密码可以通过重用每个strlen(key)输入字符的键来编码任意大的消息。这解释了为什么Vignère密码不像OTP那么安全,特别是对于短密钥,但是它更有用。
https://codereview.stackexchange.com/questions/252546
复制相似问题