Код Cs50 Vigenere дает неожиданный результат при использовании правильных значений ASCII

Я написал этот код для проблемы Виженера в cs50. Однако это не работает должным образом, когда я печатаю зашифрованные буквы, я использую 58 и 90 вместо 65 и 97. (значения ASCII для A и a)

Это дает мне желаемый результат, но я не понимаю, почему это так. При вычитании на «а» или «А» я получаю неверный зашифрованный текст, что для меня не имеет никакого смысла.

Я поискал несколько разных решений, и все они вычитали с помощью «а» или «А», чтобы перейти к алфавитному указателю, однако это не работает для меня.

Я действительно не могу понять, почему я должен использовать эти значения в моем коде, возможно, кто-нибудь может помочь мне понять, что пошло не так или где моя логическая ошибка.

Заранее спасибо.

изменить: я изменил

k [z] = tolower (k [z]); to k [z] = toupper (k [z] - 'A');

и я также изменил

printf ("% c", (((p [j] - 58) + k [l% strlen (k)])% 26) + 'A'); для printf ("% c", (((p [j] - 'A' -6) + k [l% strlen (k)])% 26) + 'A');

-6 - это потому, что это дает мне правильный результат, означающий, что что-то все еще не работает

ключ: бекон

открытый текст: Встретимся в парке в одиннадцать утра

ожидаемый результат: Negh zf av huf pcfx bt gzrwep oz

фактический вывод с -6: Negh zf av huf pcfx bt gzrwep oz

фактический вывод без -6: Tkmn fl gb nal vild hz mfxckv uf

Почему-то все значения отклоняются на -6, и я не знаю почему.

Также k.size () дал мне ошибки, поэтому я оставил strlen ().

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

//checks if only one argument was typed
if (argc != 2)
{
    printf("Error");
    return 1;
}

//assigns the keyword argv[1] to k
string k = argv[argc -1];
//check if key is alphabetical only
for (int i = 0 , n = strlen(k); i < n; i++)
{
    if(!isalpha(k[i]))
    {
        printf("Key is not alphabetical");
        return 1;
    }
}
printf("Key Valid\n");
//convert key to lowercase only
for(int z = 0; k[z]; z++)
{
    k[z] = tolower(k[z]);
}

string p = get_string("Plaintext: ");
//iterate over p
//l is incremented only when the char is alphabetical and is used as index for k
for (int j = 0,l = 0 , o = strlen(p);j < o ;j++)
{

    if(isalpha(p[j]))
    {
        if(isupper(p[j]))
        {
            //print enciphered letter
            printf("%c",(((p[j]- 58) + k[l % strlen(k)]) % 26) + 'A');
        }
        else if(islower(p[j]))
        {
            //print enciphered letter
            printf("%c",(((p[j]- 90) + k[l % strlen(k)]) % 26 ) + 'a');
        }
        //increment so that next char in k is used
        l++;
    }
    else
    {
        //print unchanged
        printf("%c",p[j]);
    }
}

return 0;
}

person D J    schedule 01.04.2018    source источник
comment
Что произойдет, если преобразовать клавишу ввода toupper вместо tolower? Заглавные буквы исторически используются по умолчанию (старый код FORTRAN был написан только заглавными буквами), поэтому буквы обычно нормализуются к верхнему регистру, а не к нижнему регистру. Но на самом деле ключ должен иметь значение 0 для буквы A, поэтому преобразование должно быть toupper(k[z]) - 'A', и после этого не должно быть задействовано никаких магических констант.   -  person Dialecticus    schedule 02.04.2018
comment
Что такое get_string функция? Каков ваш вклад? Каков ваш ожидаемый результат? Каков ваш фактический результат?   -  person Retired Ninja    schedule 02.04.2018
comment
Продолжая мой первый комментарий, если вы используете - 'A' часть, тогда вы не можете использовать strlen(k) и должны использовать k.size(), потому что нулевое значение может быть частью строки, а strlen считает нулевое значение символом конца строки, а string::size() - нет.   -  person Dialecticus    schedule 02.04.2018
comment
Я изменил k [z] = tolower (k [z]); to k [z] = toupper (k [z] - 'A'); и я также изменил printf (% c, (((p [j] - 58) + k [l% strlen (k)])% 26) + 'A'); для printf (% c, (((p [j] - 'A' -6) + k [l% strlen (k)])% 26) + 'A'); -6 - это потому, что это дает мне правильный результат, что означает, что что-то все еще не работает, я укажу ввод и вывод в описании   -  person D J    schedule 02.04.2018
comment
Также @RetiredNinja функция get_string запрашивает у пользователя строку, и я думаю, что cs50 заменил GetString () на get_string () в новом курсе 2018 года. Как я уже сказал, теперь я указал все выходы и вход в описании.   -  person D J    schedule 02.04.2018
comment
Вместо toupper(k[z] - 'A') должно быть toupper(k[z]) - 'A'. Но это означает, что если ключ имеет букву A и вы используете strlen, код будет использовать только часть ключа перед буквой. Затем вы должны использовать string::size и исследовать ошибку. Или, по крайней мере, введите новый int k_length = strlen(k) перед изменением содержимого k и используйте k_length вместо strlen(k) позже в коде.   -  person Dialecticus    schedule 02.04.2018
comment
Большое спасибо, теперь он работает. Я до сих пор не понимаю, почему использование strlen (k) не работает, но назначение его для k_length перед тем, как сделать его прописным, работает. Не могли бы вы это объяснить? И еще раз спасибо: D   -  person D J    schedule 02.04.2018
comment
strlen функция считает байты от указанного указателя вперед, пока не найдет нулевой символ. С помощью части - 'A' мы фактически превращаем предыдущий A char в нулевой символ (который не то же самое, что '0' char; более распространенное имя для этого нулевого символа - символ NUL). Вызов strlen для этого измененного указателя k прекращается преждевременно.   -  person Dialecticus    schedule 03.04.2018


Ответы (1)


Проблема с кодом в том, что ключ не был должным образом нормализован. Вместо буквы «A», обозначающей значение 0 в ключе, она обозначает значение 97. Используя ваш пример, мы должны применить значение ключа для «b» к букве «M», чтобы получить закодированную букву «N». Поскольку 'b' не 1, а 98, мы должны компенсировать это, вычитая на 97. Но поскольку мы добавляем 'A' в конце, мы должны компенсировать и это, вычитая на 65. Составное вычитание составляет 97 + 65 = 162 , модуль которого равен 162 по модулю 26 = 6. Таким образом, вы могли бы вычесть 6 вместо вычитания 58. Результат тот же, поскольку 58 по модулю 26 также 6, поэтому вы получите тот же результат, используя 58 вместо 6.

Следующий случай имеет дело со строчными буквами, поэтому сложное вычитание составляет 97 + 97 = 194, а 194 mod 26 = 12. Но так же 90 mod 26 = 12, поэтому вы получите желаемый результат, используя 90 вместо 12.

Чтобы избежать смещения, необходимого для ключа (97), вы можете создать ключ с правильными значениями (значение 0 для буквы «A»). Но вы должны сохранить длину ключа перед построением ключа, потому что функция strlen перестанет считать при значении 0 (предыдущая буква «A»). После этого еще есть смещение для регистра (65 и 97) при кодировании букв, но их значения как минимум менее запутаны.

person Dialecticus    schedule 03.04.2018
comment
Большое спасибо, я проголосовал за, но мой ранг / уровень недостаточно высок, чтобы это можно было показать. Большое спасибо за то, что помогли мне понять, что пошло не так, где и почему: D - person D J; 03.04.2018