简体   繁体   English

CS50 Vigenere,代码几乎完成了,但是我不知道缺少了什么?

[英]CS50 Vigenere, code is almost done but I don't know what's missing?

#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

int main(int argc, string argv[])
{
    // two arguments
    if (argc != 2)
    {
        printf("Give two arguments\n");
        return 1;
    }
    printf("plaintext: ");
    string plaintext = get_string();
    printf("ciphertext: ");

    string key = argv[1];

    for (int i = 0, t = 0, n = strlen(plaintext); i < n; i++, t++)
    {
        // if it's no letter, then:

        if (!isalpha(plaintext[i]) && plaintext[i] != ' ')
        {
            printf("False");

            return 1;

        }

        int number = 0;

        if (isalpha(plaintext[i]))
        {
            number += 1;
        }

        if (strlen(key) > number)
        {
            number = 0;
        }


        if (isupper(plaintext[i]))
        {
            printf("%c", (((plaintext[i] - 65) + key[number]) % 26) + 65);
        }

        //if it is lowercase
        else if (islower(plaintext[i]))
        {
            printf("%c", (((plaintext[i] - 97) + key[number]) % 26) + 97);
        }

        else
        {
            printf("%c", plaintext[i]);
        }

    }
    printf("\n");
}

So there's something missing with my code. 因此,我的代码中缺少一些东西。 When I do ./vigenere baz and then type as plaintext: Hello, world! 当我执行./vigenere baz ,然后输入为纯文本: Hello, world! , I get ciphertext: ByffiFalse . ,我得到密文: ByffiFalse I should be getting iekmo, vprke! 我应该得到iekmo, vprke! Also, when I type ./vigenere hello , and then type bye as the plaintext, I get ciphertext bye too while it should be icp . 另外,当我键入./vigenere hello ,然后键入bye作为纯文本时,我也得到了密文bye而它应该是icp Can someone figure out what's missing or wrong with my code? 有人可以找出我的代码遗漏或存在的错误吗?

The biggest two problems with your code are the calculating the correct key differential value (you're not), and key advancement. 您的代码最大的两个问题是计算正确的键差值(不是)和键提升。 I'll talk about them in reverse order. 我将以相反的顺序讨论它们。

Key advancement should start with the first key character, then advance one by one with each plain text being processed. 按键前进应从第一个按键字符开始,然后对每个要处理的纯文本逐个前进。 When the key position reaches end-of-string, it is restarted. 当键位置到达字符串末尾时,它将重新启动。 The most basic pseudo code for that would be 最基本的伪代码是

char *keyp = argv[1];

for (loop through plainttext)
{
    if (*keyp == 0) // reached the terminator ?
        keyp = argv[1]; // then reset to beginning.

   //... process the current plain text character, using *keyp
   //...  as the next key character to use.

   // advance key to next position (possibly conditionally)
   ++keyp;
}

But your code doesn't do that. 但是您的代码无法做到这一点。 Rather, it advances the key immediately, meaning you're starting with the second character onward. 相反,它会立即使键前进,这意味着您将从第二个字符开始。

int number = 0;

if (isalpha(plaintext[i]))
{
    number += 1; // HERE. first pass will use key[1]. it should be key[0]
}

if (strlen(key) > number) // this is backward
{
    number = 0;
}

Secondly, and probably more important, the whole point if a Vigenere cipher is effectively using a square shading table. 其次,也许更重要的是,如果Vigenere密码有效地使用了正方形阴影表,则这是要点。 See this link for a picture of that . 有关此图片,请参见此链接 The point of the algorithm you're coding is to act like that table exists using math. 你的编码算法的一点是要存在表使用数学。 The offsets are the important part.When you do this calculation: 偏移量是重要的部分。执行此计算时:

(((plaintext[i] - 65) + key[number]) % 26) + 65

which in reality should look like this: 实际上,它应该如下所示:

(((plaintext[i] - 'A') + key[number]) % 26) + 'A'

consider what that key character addition is doing. 考虑一下关键人物的添加。 Take your example: 举个例子:

key: baz
plaintext: Hello, World!

The first ciphertext character by your calculation will be: 根据您的计算,第一个密文字符将是:

((('H' - 'A') + 'a') % 26) + 'A'

Note: the 'a' is there because your first-pass is broken by one, remember? 注意: 'a'之所以存在,是因为您的第一遍被一个破了,还记得吗? That crunches down as follows 紧缩如下

(((7) + 97) % 26) + 'A'
((105) % 26) + 'A'
(1 % 26) + 'A'
1 + 'A'
'B'

And that's exactly what you're getting. 而这正是您所得到的。 But its wrong. 但是它是错误的。 Its wrong because this is wrong: 它是错误的,因为这是错误的:

(((plaintext[i] - 'A') + key[number]) % 26) + 'A'
                         ^^^^^^^^^^^

That's the raw ascii value of the input character. 这是输入字符的原始ascii值。 What it should be is a calculated value between 1..26. 它应该是1..26之间的计算值。 In short, you're not adjusting your key input correctly. 简而言之,您没有正确调整按键输入。

Assumptive Solution 假设解决方案

The following assumes the key will always be lower-case. 以下假设密钥始终为小写。 It also fixes your first-skip logic, and decouples using cs50.h (which, frankly, I think does more harm than good). 它还可以解决您的初次跳过逻辑,并使用cs50.h解耦(坦率地说,我认为弊大于利)。 Finally it uses a `char* to track which key character is being used next. 最后,它使用一个char *来跟踪接下来要使用的关键字符。 I leave the task of supporting mixed case input keys to you: 我将支持混合大小写输入键的任务留给您:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int main(int argc, char *argv[])
{
    // two arguments
    if (argc != 2)
    {
        printf("Give two arguments\n");
        return 1;
    }

    printf("plaintext: ");
    char pt[256] = { 0 };
    if (fgets(pt, sizeof pt, stdin))
    {
        // get the plaintext length
        size_t ptlen = strlen(pt);

        // remove trailing newline if present, and adjust ptlen
        if (ptlen > 0 && pt[ptlen - 1] == '\n')
            pt[--ptlen] = 0;

        // the key we're using. intially at the start
        char *key = argv[1];

        for (size_t i = 0; i < ptlen; ++i)
        {
            // reset key if prior iteration landed on terminator
            if (!*key)
                key = argv[1];

            if (isalpha((unsigned char)pt[i]))
            {
                if (isupper((unsigned char)pt[i]))
                {
                    printf("%c", (((pt[i] - 'A') + (*key-'a')) % 26) + 'A');
                    ++key;
                }

                //if it is lowercase
                else if (islower((unsigned char)pt[i]))
                {
                    printf("%c", (((pt[i] - 'a') + (*key-'a')) % 26) + 'a');
                    ++key;
                }
                else
                {
                    fputc(pt[i], stdout);
                }
            }
            else
            {
                fputc(pt[i], stdout);
            }
        }

        fputc('\n', stdout);
    }
    else
    {
        perror("Failed to read string");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;

}

Output from ./progname baz ./progname baz输出

plaintext: Hello, World!
Iekmo, Vprke!
  1. All non-alpha characters (not spaces only) should be skipped without encoding. 所有非字母字符(不仅限于空格)都应跳过而不进行编码。 Do not print "False" and return on, for example ',' symbol in "Hello, world!" 不要打印"False"并返回,例如"Hello, world!"中的"Hello, world!" ','符号"Hello, world!" string. 串。 Also, you can encode string in-place. 另外,您可以就地编码字符串。 Thus, main loop may looks like 因此,主循环可能看起来像
printf("plaintext: ");
string s = GetString();
if (s == NULL)
    return 1;

for (int i = 0, len = strlen(s); i < len; ++i) {
    if (isalpha(s[i])) {
        /* encode s[i] in-place,
         * all non-alpha characters left as is
         */
    }
}

printf("ciphertext: %s\n", s);
  1. Key characters should also be "shifted". 关键字符也应“转移”。 For example, for uppercase letters 例如,对于大写字母
s[i] = ((s[i] - 'A') + (key[n] - 'A') % 26) + 'A';
if (++n >= keylen)
    n = 0;

I suggest to normalize key before main loop, so that you will be able to use (key[n] - 'A') both for lower and upper characters from input string: 我建议在主循环之前对key进行规范化,这样您就可以将(key[n] - 'A')用作输入字符串中的小写和大写字符:

string key = argv[1];
strupper(k);
int keylen = strlen(key);
int n = 0;

Although I don't want provide full code because this is your courses, I think it would be better if you do it by yourself. 尽管我不想提供完整的代码,因为这是您的课程,但我认为,如果您自己这样做,会更好。 But… some pieces: 但是……一些碎片:

strupper function: strupper功能:

void strupper(string s)
{
    for (int i = 0, n = strlen(s); i < n; ++i)
        s[i] = toupper(s[i]);    
}

Compact main loop: 紧凑的主循环:

for (int i = 0, n = strlen(s); i < n; ++i) {
    if (isalpha(s[i])) {
        char ref = isupper(s[i]) ? 'A' : 'a';
        int shift = k[j] - 'A';
        s[i] = ref + (s[i] - ref + shift) % 26;
        if (++j >= klen) j = 0;
    }
}

ps You use the same key character for all input characters because of int number = 0; ps因为int number = 0;所以对所有输入字符都使用相同的键字符int number = 0; defined and zeroed inside for loop. for循环中定义并归零。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM