Самыми большими проблемами в вашем коде являются вычисление правильного ключевого дифференциального значения (а вы нет) и продвижение ключа.Я расскажу о них в обратном порядке.
Продвижение ключа должно начинаться с первого ключевого символа, а затем переходить один за другим при обработке каждого простого текста.Когда ключевая позиция достигает конца строки, она перезапускается.Самый простой псевдокод для этого будет
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!