首页 > 解决方案 > 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!, I get ciphertext: ByffiFalse. I should be getting 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. Can someone figure out what's missing or wrong with my code?

标签: ccs50

解决方案


您的代码最大的两个问题是计算正确的密钥差异值(您不是)和密钥推进​​。我将按相反的顺序讨论它们。

键前进应该从第一个键字符开始,然后一个接一个地前进,处理每个纯文本。当关键位置到达字符串末尾时,它会重新启动。最基本的伪代码是

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;
}

但是您的代码没有这样做。相反,它会立即推进键,这意味着您从第二个字符开始。

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;
}

其次,也许更重要的是,如果 Vigenere 密码有效地使用了方形着色表,那么这一点很重要。请参阅此链接以获取图片。您正在编码的算法的重点是使用数学来表现该表的存在。偏移量是重要的部分。当您进行此计算时:

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

实际上应该是这样的:

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

考虑添加关键字符的作用。举个例子:

key: baz
plaintext: Hello, World!

您计算的第一个密文字符将是:

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

注意:'a'那里是因为你的第一次通过被打破了,记得吗?紧缩如下

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

这正是你得到的。但它错了。这是错误的,因为这是错误的:

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

那是输入字符的原始 ascii 值。它应该是 1..26 之间的计算值。简而言之,您没有正确调整按键输入。

假设解决方案

以下假设密钥始终为小写。它还修复了您的 first-skip 逻辑,并使用 cs50.h 解耦(坦率地说,我认为这弊大于利)。最后,它使用 `char* 来跟踪接下来使用哪个关键字符。我将支持混合大小写输入键的任务留给您:

#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;

}

输出自./progname baz

plaintext: Hello, World!
Iekmo, Vprke!

推荐阅读