О 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]
). Я оставляю фактический код для вас.