在 C 中无循环地更改和包装关键字整数

Change and wrap keyword integers without loop in C

我正在编写一个程序,它在命令提示符处接受一个字符串,然后将字符串的每个字符转换为字母表中相应的 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] == '[=11=]')
    {
        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;
}

感谢任何帮助和建议,但请记住,解决方案应符合我的程序建议的编码水平。可能有许多高级方法可以编写此程序,但不幸的是,我们仍处于本课程的开始阶段,因此展示新方法(我一定会尝试理解)可能会超出我的理解范围。

这是您的代码的修改版本,根据我的评论进行了更改:

#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] == '[=10=]')
    {
        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;
}

恰好两个参数和非空键的测试被移到顶部。这与评论中的建议略有不同。错误消息打印到标准错误,而不是标准输出。我可能会将第二条 'usage' 消息替换为更具体的错误 — the key may only contain alphabetic characters 或类似的错误。并且错误应该包括 argv[0] 作为程序名称,而不是硬编码名称。密钥验证循环检查密钥是否全部为字母,而不是检查它们是否不是数字——字符 类 多于数字和字母。该代码使用 keyidxkeylen 来跟踪密钥的长度和密钥中的位置。我使用单字母变量名,但通常只用于循环索引或简单指针(通常是指向字符串的指针);否则我使用简短的半助记名称。对 shift() 有两次调用,因此 keyidx 仅在输入字符为字母时递增。还有其他编码方式。

评论中没有预料到的一个非常重要的变化是 t 的类型变化——从 charint。当它是 char 时,如果您用字母表中后期的字母(例如 y)对字母 z 进行加密,则值 'z' + 24 会溢出(带符号的)char 类型在 Intel 机器上很普遍,给出一个负值(最典型的;形式上,行为是未定义的)。这会导致虚假输出。更改为 int 可解决该问题。由于 t 的值在传递给 printf() 时无论如何都会提升为 int,因此在打印时不会造成任何损害。我使用提示 plain text: 和 space 以便输入和输出在页面上对齐。

我决定不在shift()中使用额外的局部变量k1。如评论中所述,我还使用了减法而不是模数。

给定从 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 行中的 'encrypting' 个字母与数据第 2 行中的解密字母进行匹配得出的:

abcdefghijklmnopqrstuvwxyz
azyxwvutsrqponmlkjihgfedcb

对于加密和解密,代码最基本的酸性测试是程序可以在给定正确的解密密钥和密文的情况下解密自己的加密输出。