PSET 2: Vigenere Cipher работает частично?

Я создал следующий код в качестве ответа на CS50x PSET2: Vigenere, и он в некоторой степени работает, однако при запуске check50 я получаю несколько ошибок, перечисленных ниже:

:) vigenere.c exists.
:) vigenere.c compiles.
:) encrypts "a" as "a" using "a" as keyword
:( encrypts "barfoo" as "caqgon" using "baz" as keyword - output not valid ASCII text 
:( encrypts "BaRFoo" as "CaQGon" using "BaZ" as keyword - output not valid ASCII text 
:) encrypts "BARFOO" as "CAQGON" using "BAZ" as keyword
:( encrypts "world!$?" as "xoqmd!$?" using "baz" as keyword- output not valid ASCII text 
:( encrypts "hello, world!" as "iekmo, vprke!" using "baz" as keyword- output not valid ASCII text 
:) handles lack of argv[1]
:) handles argc > 2
:( rejects "Hax0r2" as keyword - timed out while waiting for program to exit 

Кажется, происходит то, что ключ содержит высокое значение (например, z / Z), это заставляет код переходить к следующей строке и пропускать то, что кажется случайными последовательностями. например. в первом слове строки пропущен 3-й символ, затем во втором слове пропущены 3-й и 4-й, а затем в третьем слове 1-й. Я просто не могу понять, что происходит.

Я использовал printf, чтобы убедиться, что все переменные, которые устанавливаются и передаются в функции, верны во время выполнения. Сами функции возвращают правильные ответы (кроме проверки Hax0r2). Я пробовал отладку, сравнивая результаты с онлайн-инструментом шифрования vigenere.

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

int Validate1(int argc);
int Validate2(string argv);
void Cypher(string x);
void KeyCalc(string argv);

string MESSAGE;
int LENGTH;
int *KEY;
int COUNTER = 0;

int main(int argc, string argv[])
{
    //Check if right amount of arguments are supplied
    int Val1 = Validate1(argc);

    if (Val1 == 0)
    {
        //Check if argument is a string of chars
        int Val2 = Validate2(argv[1]);

        if (Val2 == 0)
        {
            //get the string length
            LENGTH = strlen(argv[1]);

            //Dynamically update KEY array length
            KEY = (int *)malloc(LENGTH * sizeof(*KEY));
            if (KEY == NULL)
            {
                fprintf(stderr, "malloc failed\n");   
            }

            //calculate the key
            KeyCalc(argv[1]);

            //get the message from the user to be encrypted
            MESSAGE = get_string("plaintext: ");
            printf("ciphertext: ");

            //encrypt message from user
            Cypher(argv[1]);
            free(KEY);
            return 0;
        }
        else
        {
            //validation failed
            printf("Usage: ./vigenere keyword\n");
            return 1;
        }
    }
    else
    {
        //validation failed
        printf("Usage: ./vigenere keyword\n");
        return 1;
    }
}

//Validate the number of arguments supplied
int Validate1(int argc)
{
    if (argc != 2)
    {
        return 1;
    }
    else
    {
        return 0;   
    }
}

//Validate the argument is a string
int Validate2(string argv)
{
    int k = 0;

    //loop through all characters in argument line string and check if alphabetic 
    for (int i = 0; i < LENGTH; i++)
    { 
        if isalpha(argv[i])
        {
            //Do Nothing
        }
        else
        {
            k++; 
        }
    }

    //k counts the number of non-alphabetic characters, so if > 0 then invalid input
    if (k > 0)
    {
        return 1;
    }
    else
    {
        return 0;    
    }
}


void Cypher(string x)
{
    //identify the length of the message to be coded
    int Mlength = strlen(MESSAGE);

    //identify the length of the key
    int Slen = strlen(x);

    //cycle through all characters in message supplied by user
    for (int i = 0; i < Mlength; i++)
    {
        // loop through key
        if (COUNTER > Slen - 1)
        {
            COUNTER = 0;
        }
        //check if the character is alphabetic
        if (isalpha(MESSAGE[i]))
        {
            //convert the character to ASCII int value
            char l = MESSAGE[i];

            //add key value to message value and wrap around ascii mapping
            if (isupper(MESSAGE[i]))
            {
                l = l + KEY[COUNTER];
                if (l > 'Z')
                {
                    l = l - 26;    
                }
            }
            else
            {
                l = l + KEY[COUNTER];
                if (l > 'z')
                {
                    l = l - 26;    
                }    
            }

            //convert value back into character and store in array
            MESSAGE[i] = (char) l;
            // print character 
            printf("%c", MESSAGE[i]);
            COUNTER++;
        }
        else
        {
            //character is 'numeric' or 'symbol' or 'space' just display it
            printf("%c", MESSAGE[i]);
        }
    }
    printf("\n");
}

void KeyCalc(string argv)
{
    //convert key entry to values A/a = 0 to Z/z = 26
    for (int i = 0; i < LENGTH; i++)
    {
        char k = argv[i];
        if (islower(argv[i]))
        {
            KEY[i] = k - 'a'; 
        }
        else
        {
            KEY[i] = k - 'A'; 
        }      
    }    
}
  • шифрует barfoo как caqgon с использованием ключевого слова baz
  • шифрует «BaRFoo» как «CaQGon», используя «BaZ» в качестве ключевого слова
  • шифрует "world! $?" как "xoqmd! $?" используя "baz" как ключевое слово
  • шифрует "привет, мир!" как "иекмо, впрке!" используя "baz" как ключевое слово
  • отклоняет "Hax0r2" как ключевое слово

person Dan Sutton    schedule 03.08.2019    source источник


Ответы (1)


Из спецификации для cesar pset:

... Алгоритм Цезаря (т.е. шифр) шифрует сообщения, «вращая» каждую букву на k позиций. Более формально, если p - некоторый открытый текст (т. Е. Незашифрованное сообщение), p i - i-й символ в p, а k - секретный ключ (т. Е. Неотрицательное целое число), то каждый буква c i в зашифрованном тексте c вычисляется как

c i = (p i + k)% 26

Этот алгоритм (в любом «случае») не делает этого:

 l = l + KEY[COUNTER];
                if (l > 'Z')
                {
                    l = l - 26;    
                }

Это пошаговое руководство начинается в 9:30. хороший праймер о том, как осуществить «смену».

Ближайшая причина проблемы в этом коде заключается в том, что этот l = l + KEY[COUNTER]; может давать результат за пределами диапазона ascii. В реализации CS50 char по умолчанию используется подписанный символ. Так, например, 'r' + 'z' (как в "barfoo", зашифрованном с помощью "baz") даст -117.

person DinoCoderSaurus    schedule 03.08.2019
comment
Спасибо, что указали на это. Теперь он у меня работает отлично. - person Dan Sutton; 04.08.2019