CS50 Vigenere, код почти готов, но я не знаю, чего не хватает? - PullRequest
0 голосов
/ 14 сентября 2018
#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");
}

Так что с моим кодом чего-то не хватает. Когда я набираю ./vigenere baz, а затем набираю как текст: Hello, world!, я получаю шифротекст: ByffiFalse. Я должен получить iekmo, vprke! Кроме того, когда я набираю ./vigenere hello, а затем набираю bye в качестве открытого текста, я тоже получаю шифротекст bye, пока он должен быть icp. Может кто-нибудь выяснить, чего не хватает или что не так с моим кодом?

Ответы [ 2 ]

0 голосов
/ 14 сентября 2018
  1. Все не-буквенные символы (не только пробелы) должны быть пропущены без кодирования. Не печатайте "False" и не возвращайте, например, символ ',' в строке "Hello, world!". Кроме того, вы можете кодировать строку на месте. Таким образом, основной цикл может выглядеть как
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. Ключевые символы также должны быть «сдвинуты». Например, для заглавных букв
s[i] = ((s[i] - 'A') + (key[n] - 'A') % 26) + 'A';
if (++n >= keylen)
    n = 0;

Я предлагаю нормализовать ключ перед основным циклом, чтобы вы могли использовать (key[n] - 'A') как для нижних, так и для верхних символов входной строки:

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

Хотя я не хочу предоставлять полный код, потому что это ваши курсы, я думаю, было бы лучше, если бы вы делали это самостоятельно. Но ... некоторые кусочки:

strupper функция:

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

Компактная основная петля:

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

p.s. Вы используете один и тот же символ клавиши для всех вводимых символов, поскольку int number = 0; определено и обнулено внутри цикла for.

0 голосов
/ 14 сентября 2018

Самыми большими проблемами в вашем коде являются вычисление правильного ключевого дифференциального значения (а вы нет) и продвижение ключа.Я расскажу о них в обратном порядке.

Продвижение ключа должно начинаться с первого ключевого символа, а затем переходить один за другим при обработке каждого простого текста.Когда ключевая позиция достигает конца строки, она перезапускается.Самый простой псевдокод для этого будет

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

Но ваш код этого не делает.Скорее, он немедленно продвигает ключ, то есть вы начинаете с символа second и далее.

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.Короче говоря, вы неправильно настраиваете ввод с клавиатуры.

Предполагаемое решение

Ниже предполагается, что ключ всегда будет в нижнем регистре.Он также исправляет вашу логику первого пропуска и разъединяет использование 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!
...