在 C 中更改和包装关键字整数而无需循环
Posted
技术标签:
【中文标题】在 C 中更改和包装关键字整数而无需循环【英文标题】:Change and wrap keyword integers without loop in C 【发布时间】:2019-10-13 16:19:43 【问题描述】:我正在编写一个程序,它在命令提示符处接受一个字符串,然后将字符串的每个字符转换为相应的字母表中的 0-25 位。然后,每个数字用于加密用户在程序提示后输入的另一个字符串的每个字符。第二个字符串的每个字母字符应与整数字符串的顺序匹配,如果第二个字符串较长,整数字符串将换行。该程序的目标是使用第一个字符串作为键来移动消息的每个字符(第二个字符串)。
示例(期望的输出):
用户运行程序并输入关键字:bad
提示用户仅输入字母字符串和标点符号:Dr. Oz
程序将关键字 'bad' 转换为 1,0,3
程序将消息加密成Er. Ra
我实际得到的是:
… T.B.S. …
我已经尝试了很多事情,但不幸的是,我似乎无法弄清楚如何在不循环第二条消息的情况下循环和包装密钥。如果你运行程序,你会看到我的问题。
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int shift(char key1);
int main(int argc, string argv[]) // user enter number at cmd prompt
if (argv[1] == '\0')
printf("Usage: ./vigenere keyword\n");
return 1;
string key = argv[1]; // declare second arg as string
for (int i = 0, n = strlen(key); i < n; i++)
if (isdigit(key[i]) != 0 || argc != 2)
printf("Usage: ./vigenere keyword\n");
return 1;
string text = get_string("plaintext: ");
printf("ciphertext: ");
int k;
char t;
for (int j = 0, o = strlen(text); j < o; j++)
t = text[j];
for (int i = 0, n = strlen(key); i < n; i++)
k = shift(key[i]);
if (isupper(t))
t += k;
if (t > 'Z')
t -= 26;
if (islower(t))
t += k;
if (t > 'z')
t -= 26;
printf("%c", t);
printf("\n");
int shift(char key1)
int k1 = key1;
if (islower(key1))
k1 %= 97;
if (isupper(key1))
k1 %= 65;
return k1;
感谢任何帮助和建议,但请记住,解决方案应与我的程序建议的编码水平相匹配。编写这个程序可能有许多高级方法,但不幸的是,我们仍处于本课程的开始阶段,因此展示新方法(我一定会尝试理解)可能会让我头疼。
【问题讨论】:
如果您点击vigenere,您会发现很多与您的程序相似的程序。您也许可以在其中找到解决方案。 您的行if (argv[1] =='\0')
并非完全错误,但最好使用0
或NULL
编写——'\0'
是一个非正统但有效的空指针常量。你不是在比较一个角色。您正在将字符指针与空指针进行比较。同样,下一个循环中的argc != 2
测试并非完全错误,但由于它在循环中而不必要地重复,并且在初始测试中可能会更好:if (argc != 2 || argv[1] == NULL || argv[1][0] == '\0')
(非空第一个参数的额外测试)。
您需要删除内循环的循环控制,但保留相当数量的内循环主体。您可以使用j
计算正确的键索引;您还将获得密钥的长度(称为keylen
)。您将找到与 j % keylen
一起使用的键的正确元素(因此,对于 3 个字符的键,您将获得依次使用的值 0、1、2)。您在 shift 中的代码可以使用减法而不是模运算符——我以前没有见过这种方法,但我认为它确实有效,但它可能不如k1 -= 'a';
或k1 -= 'A';
干净。您的验证可能应该使用isalpha()
进行检查。
【参考方案1】:
这是您的代码的修改版本,根据我的 cmets 进行了更改:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int shift(char key1);
int main(int argc, string argv[]) // user enter number at cmd prompt
if (argc != 2 || argv[1][0] == '\0')
fprintf(stderr, "Usage: ./vigenere keyword\n");
return 1;
string key = argv[1]; // declare second arg as string
for (int i = 0, n = strlen(key); i < n; i++)
if (!isalpha(key[i]))
fprintf(stderr, "Usage: ./vigenere keyword\n");
return 1;
string text = get_string("plain text: ");
printf("ciphertext: ");
int keylen = strlen(key);
int keyidx = 0;
for (int j = 0, o = strlen(text); j < o; j++)
int t = text[j];
if (isupper(t))
int k = shift(key[keyidx++ % keylen]);
t += k;
if (t > 'Z')
t -= 26;
else if (islower(t))
int k = shift(key[keyidx++ % keylen]);
t += k;
if (t > 'z')
t -= 26;
printf("%c", t);
printf("\n");
int shift(char key1)
if (islower(key1))
key1 -= 'a';
if (isupper(key1))
key1 -= 'A';
return key1;
恰好两个参数和一个非空键的测试被移到顶部。这与 cmets 中的建议略有不同。错误消息打印到标准错误,而不是标准输出。我可能会用更具体的错误替换第二条“使用”消息——the key may only contain alphabetic characters
或类似的错误。并且错误应该包括argv[0]
作为程序名称,而不是硬编码名称。密钥验证循环检查密钥是否全是字母,而不是检查它们是否不是数字——字符类别比数字和字母多。该代码使用keyidx
和keylen
来跟踪密钥的长度和密钥中的位置。我使用单字母变量名,但通常只用于循环索引或简单指针(通常是指向字符串的指针);否则我使用简短的半记忆名称。对shift()
有两次调用,因此keyidx
仅在输入字符为字母时递增。还有其他的编码方式。
cmets 中没有预见到的一个非常重要的变化是t
的类型变化——从char
到int
。当它是 char
时,如果您使用字母表中的字母(例如 y
)加密字母 z
,则值 'z' + 24
会溢出 Intel 机器上流行的(签名)char
类型,给出一个负值(最常见;形式上,行为未定义)。这会导致虚假输出。更改为 int
可以解决该问题。由于t
的值在传递给printf()
时无论如何都会提升为int
,因此不会对打印造成任何损害。我在提示符plain text:
中使用了空格,以便输入和输出在页面上对齐。
我决定不在shift()
中使用额外的局部变量k1
。如 cmets 中所述,我还使用减法而不是模数。
给定从cc59.c
创建的程序cc59
,示例运行是:
$ cc59 bad
plain text: Dr. Oz
ciphertext: Er. Ra
$ cc59 zax
plain text: Er. Ra
ciphertext: Dr. Oz
$ cc59 ablewasiereisawelba
plain text: The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs. The five boxing wizards jump quickly. How vexingly quick daft zebras jump. Bright vixens jump; dozy fowl quack.
ciphertext: Tip uqius fisef fkb uvmpt zzar lpi cehq dkk. Abck nj fkx oqxy jqne zskfn ljbykr bckj. Xpw fezp coxjyk sirivuw rmml ufjckmj. Lkw nmbzrody mytdk dbqx vetzej ncep. Xvthht wtbank rydt; lgzu jzxl qvlgg.
$ cc59 azpweaiswjwsiaewpza
plain text: Tip uqius fisef fkb uvmpt zzar lpi cehq dkk. Abck nj fkx oqxy jqne zskfn ljbykr bckj. Xpw fezp coxjyk sirivuw rmml ufjckmj. Lkw nmbzrody mytdk dbqx vetzej ncep. Xvthht wtbank rydt; lgzu jzxl qvlgg.
ciphertext: The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs. The five boxing wizards jump quickly. How vexingly quick daft zebras jump. Bright vixens jump; dozy fowl quack.
$
解密密钥是通过将第 1 行中的“加密”字母与数据第 2 行中的解密字母进行匹配得出的:
abcdefghijklmnopqrstuvwxyz
azyxwvutsrqponmlkjihgfedcb
通过加密和解密,对代码最基本的酸性测试是程序可以在给定正确的解密密钥和密文的情况下解密自己的加密输出。
【讨论】:
编码很有趣。仅这一件就使程序正常工作。 int k = shift(key[keyidx++ % keylen]) 谢谢乔纳森!你教会了我一些新东西。以上是关于在 C 中更改和包装关键字整数而无需循环的主要内容,如果未能解决你的问题,请参考以下文章