Vigenere CS50 - Нужна помощь с циклическим переключением буквенных букв

Я пытаюсь выполнить упражнение CS50 Vigenere.

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

int main(int argc, string argv[])
{
//Check for 2 command line arguments
if (argc != 2)
{
    printf("Nah bro, you gotta have 2 arguments.\n");
    return 1;
}
//Check is alpha
else {
    for (int i = 0; i < strlen(argv[1]); i++)
    {
        if (isalpha(argv[1][i]) == 0)
        {
            printf("Nah bro, u gots to use letters.\n");
            return 1;
        }
    }
}


//Prompt user to input text
    printf("plaintext: ");
    string p = get_string();

//Cipher
    printf("ciphertext: ");
    string k = argv[1];
    int cipherlen = strlen(k);



//Cycle through key letters
for (int i = 0, j = 0, n = strlen(p); i < n; i++)
{


    if (isalpha(p[i]))
    {

        if (isupper(p[i]))
            {
            printf("%c", ((p[i] - 65) + (k[(j % cipherlen)]) - 65) % 26 + 65);
            j++;
            }


        else if (islower(p[i]))
            {
            printf("%c", ((p[i] - 97) + (k[(j % cipherlen)]) - 97) % 26 + 97);
            j++;
            }


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

    }
        printf("\n");
        return 0;
}

Вот мои коды ошибок по проверке:

https://cs50.me/checks/a56bc9325327035cb0e8d831693c9805c4b6468b

Я понимаю, что моя проблема связана с циклическим перебором каждой буквы, но не с применением ее к пробелам или символам. Я пробовал использовать оператор if (isalpha) и else printf (""), но он не работает с числами или символами. Я полагал, что добавление j ++ будет повторяться только через альфа-символы, но, похоже, это не помогает.

Есть ли здесь что-то очень простое, что мне не хватает?


person Leo H    schedule 17.06.2018    source источник
comment
строка, похоже, имеет тип char *, так что, вероятно, все в порядке ...   -  person Ctx    schedule 17.06.2018
comment
@melpomene Он должен работать для кодов ascii ‹128, так что здесь все в порядке.   -  person Ctx    schedule 17.06.2018
comment
@LeoH Вы должны использовать isprint() вместо isalpha(), чтобы также выводить символы, такие как "$! и т. Д.   -  person Ctx    schedule 17.06.2018


Ответы (1)


Основная структура вашего кода выглядит нормально.

Я вижу в этом три проблемы:

  1. Все ваши printf защищены проверкой if (isalpha(p[i])), поэтому ваша программа никогда ничего не выводит, если символ открытого текста не является буквенным (вместо этого он должен выводить символ без изменений). Исправить это просто; просто удалите внешний if (...) в цикле:

    for (int i = 0, j = 0, n = strlen(p); i < n; i++)
    {
        if (isupper(p[i]))
            {
            printf("%c", ((p[i] - 65) + (k[(j % cipherlen)]) - 65) % 26 + 65);
            j++;
            }
        else if (islower(p[i]))
            {
            printf("%c", ((p[i] - 97) + (k[(j % cipherlen)]) - 97) % 26 + 97);
            j++;
            }
        else
            printf ("%c", p[i]);
    }
    

    Внутренняя цепочка _5 _ / _ 6_ правильно обрабатывает этот случай.

  2. Текущий символ открытого текста p[i] и текущий символ ключевого слова k[j % cipherlen] могут быть прописными / строчными буквами независимо друг от друга. Ваш код в настоящее время вообще не справляется с этим; вместо этого предполагается, что если p[i] - это верхний регистр, k[j % cipherlen] также должен быть прописным, и аналогично для нижнего регистра.

    Кстати, не рекомендую писать в коде 65 и 97. Я бы использовал вместо этого 'A' и 'a' соответственно, что делает вещи более читабельными, ИМХО.

    Чтобы решить эту проблему, вам нужно протестировать k[j % cipherlen] для прописных и строчных букв отдельно. Например:

    for (int i = 0, j = 0, n = strlen(p); i < n; i++)
    {
        char key_char = k[j % cipherlen];
        int key_shift;
        if (isupper(key_char)) {
            key_shift = key_char - 'A';
        } else {
            key_shift = key_char - 'a';
        }
        if (isupper(p[i]))
            {
            printf("%c", ((p[i] - 'A') + key_shift) % 26 + 'A');
            j++;
            }
        else if (islower(p[i]))
            {
            printf("%c", ((p[i] - 'a') + key_shift) % 26 + 'a');
            j++;
            }
        else
            printf ("%c", p[i]);
    }
    

    (Мне надоело многократно вводить одни и те же выражения, поэтому я извлек общие биты в переменные (key_char, key_shift). Единственная сложная часть здесь заключается в том, что j следует увеличивать только в том случае, если key_shift действительно используется, но ваш код уже обрабатывает это .)

  3. Это тонкий момент, но все функции <ctype.h> (такие как isupper, isalpha, ...) имеют неопределенное поведение, если аргумент отрицательный. char является типом со знаком во многих реализациях, поэтому случайный символ str[i] вполне может быть отрицательным. Чтобы быть полностью переносимым и правильным, вы должны приводить символ к (unsigned char) в каждом таком вызове:

    if (isupper((unsigned char)key_char))
        ...
    if (isupper((unsigned char)p[i]))
        ...
    else if (islower((unsigned char)p[i]))
        ...
    

    В качестве альтернативы просто полностью используйте ASCII (остальная часть вашего кода уже предполагает это) и выполните:

    if (key_char >= 'A' && key_char <= 'Z')
        ...
    if (p[i] >= 'A' && p[i] <= 'Z')
        ...
    else if (p[i] >= 'a' && p[i] <= 'z')
        ...
    
person melpomene    schedule 17.06.2018
comment
Вот это да. Большое спасибо. Вы полностью помогли мне увидеть вещи в новом свете. - person Leo H; 19.06.2018
comment
Однако вопрос - есть ли причина, по которой мы используем key_shift = key_char - 'a', чем скажем tolower ()? - person Leo H; 19.06.2018