О CS50 pset2 Vigenere

Почему мой код не пропустил пробел, и это привело к неправильной последовательности шифрования?

Когда я проверяю пример "Hello, World!", Мой код также подсчитывает пространство и преобразуется в "Iekmo, Wnslc!" вместо "Иэкмо, Впрке!" используя ключ "баз"

Может кто-нибудь объяснить логику позади? Большое спасибо!

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

//getting user encryption key


int main(int argc, string argv[])
{

    if (argc != 2)
    {
        printf("Usage: ./vigenere keyword\n");
        return 1;
    }
    //check if all are alphabeticals
    else 
    {
           for (int i = 1; i < argc; i++)
        {
            for (int j = 0; j < strlen(argv[i]); j++)
            {
                if (isalpha(argv[i][j]) == false)
                {                
                    printf("Usage: ./vigenere keyword\n");
                    return 1;
                }
            }
        }
    }  


    //getting plaintext divide it into each character
    string pt = get_string("plaintext: ");

    printf("ciphertext: ");
    //convert to ciphertext
    //C = (P + k) % 26
    for (int r = 0; r < strlen(pt); r++)
    {                           
        if (isupper(pt[r]))
        {
            //making loop with j group corresponding to keyword
            int j = r % strlen(argv[1]);
            int key = tolower(argv[1][j]) - 97;
            printf("%c", (pt[r] - 65 + key) % 26 + 65);
        }
        else if (islower(pt[r]))
        {
            //making loop with j group corresponding to keyword
            int j = r % strlen(argv[1]);
            int key = tolower(argv[1][j]) - 97;
            printf("%c", (pt[r] - 97 + key) % 26 + 97);
        }
        else
        {
            printf("%c", pt[r]);
        }

     }            
       printf("\n");       

}

2 ответа

Это гораздо проще показать, чем объяснить:

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

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s keyword\n", argv[0]);
        return 1;
    }

    for (int i = 1; i < argc; i++)
    {
        for (int j = 0; argv[i][j] != '\0'; j++)
        {
            if (isalpha(argv[i][j]) == false)
            {                
                fprintf(stderr, "%s: non-alphabetic character '%c' (%d) in key\n",
                        argv[0], argv[i][j], argv[i][j]);
                return 1;
            }
        }
    }

    string pt = get_string("plaintext:  ");

    printf("ciphertext: ");
    int k = 0;
    int keylen = strlen(argv[1]);
    for (int r = 0; pt[r] != '\0'; r++)
    {                           
        if (isupper(pt[r]))
        {
            int j = k++ % keylen;
            int key = tolower(argv[1][j]) - 'a';
            printf("%c", (pt[r] - 'A' + key) % 26 + 'A');
        }
        else if (islower(pt[r]))
        {
            int j = k++ % keylen;
            int key = tolower(argv[1][j]) - 'a';
            printf("%c", (pt[r] - 'a' + key) % 26 + 'a');
        }
        else
        {
            printf("%c", pt[r]);
        }

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

Пример выполнения:

$ ./vig89 baz
plaintext:  Hello, World!
ciphertext: Iekmo, Vprke!
$

Как я отметил в комментарии, вам нужно отделить "положение в строке, r из "зашифрованного номера символа". Вам нужна дополнительная переменная, которую вы увеличиваете, только если символ алфавитный.

В приведенном выше коде k это дополнительная переменная (keylen это другой, но он просто записывает длину ключа, а не многократно вызывая strlen()). Значение в k увеличивается, когда известно, что символ является буквой, а не иначе.

Я заметил, что это может быть разумным для процесса argv[1] так что вам не нужно делать tolower() конвертация каждый раз; Вы можете сделать это во время проверки ключевого слова.

Я также сообщил об ошибках по стандартной ошибке, и не используется strlen() в условной части петли. Хотя стоимость не слишком высока, если вы имеете дело с трехбуквенными ключами, если вы начнете вычислять длину строки длиной 20 КиБ на каждой итерации, вы можете начать обнаруживать накладные расходы (если компилятору не удастся их оптимизировать) - может, а может и нет). Я также выровнял простой текст и зашифрованный текст в I/O.

Есть довольно много других изменений, которые можно / нужно сделать. Например, первый for (int i = 1; …) петля не нужна; у вас есть только один аргумент, поэтому вам нужен только внутренний for (int j = 0; …) петля. Это также более идиоматично для использования if (!isalpha(argv[i][j])) чем сравнить результат с false тем более что isalpha макрос не гарантированно возвращает 0 или 1 (он возвращает ноль или не ноль) - поэтому изменение if (isalpha(argv[i][j]) == false) в if (isalpha(argv[i][j] != true) не будет надежным. Я бы наверное создал простую переменную char *key = argv[1]; (или же string key = argv[1]; в контексте CS50, хотя я не уверен, что CS50 typedef char *string; это хорошая идея) и использовать это в программе.

Проблема с расчетом J здесь int j = r % strlen(argv[1]);, Индекс ключа не связан с r (указатель сообщения). Программа должна перебирать ключ, основываясь исключительно на (длине) ключа. Вам нужно увеличивать его каждый раз, когда вы "используете" ключевой индекс и "оборачиваете" его, чтобы он не проходил до конца. Вы могли бы рассмотреть объявление j перед r петля; увеличивайте j всякий раз, когда вы используете ключевой индекс (подсказка: j++); и "обернуть" j с оператором по модулю (подсказка: j % strlen(argv[1]). Я оставляю фактический код для вас.

Другие вопросы по тегам