CS50 Pset 2-Ceasar шифр - PullRequest
       19

CS50 Pset 2-Ceasar шифр

1 голос
/ 23 мая 2019

Это не показывает то, что я хочу, чтобы это была зашифрованная версия входного текста, но вместо этого символы, как я полагаю, выглядят как '?'выходит как выход в терминал.Может ли кто-нибудь помочь мне найти то, что я пропустил или сделал неправильно?

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

int main(int argc, string argv[])
{
    if (argc == 2)
    {
        string ptext = get_string("plaintext: ");
        int key = (int) argv[1];
        printf("ciphertext: ");
        for (int i = 0, n = strlen(ptext); i < n; i++)
        {
            printf("%c", (( ptext[i] + key ) % 26);
        } 
        printf("\n");
    }
    else
    {
        printf("Invalid input. \n");
    }

}

Я ожидаю, что вывод 'hello' будет 'ifmmp', но вместо этого это не так.

Ответы [ 2 ]

0 голосов
/ 23 мая 2019

Проблема здесь printf("%c", (( ptext[i] + key ) % 26);.В частности, с % 26.Это, безусловно, выглядит точно так же, как и для поставленной задачи:

Более формально, если p - это какой-то открытый текст (т.е. незашифрованное сообщение), pi - это i-й символ в p, а k - секретный ключ (т.е.(неотрицательное целое число), то каждая буква ci в зашифрованном тексте c вычисляется как

ci = (pi + k)% 26

, где% 26 здесь означает «остаток от деления на 26. ”

Но, псхет продолжает:

думать о A (или a) как 0, B (или b) как1,…, H (или h) как 7, I (или i) как 8,… и Z (или z) как 25.

Проблема в том, что символ ptext[i] ascii значение буквы , а не «алфавитный указатель».

Возможно, рассмотрите спойлер в разделе псевдокода Лаборатории, в частности, # 5:

Перебирайте каждый символ открытого текста:

  • Если этоявляется заглавной буквой, поверните ее, сохранив регистр, затем распечатайте повернутый символ
  • Если это строчная буква, поверните ее, сохранив регистр, а затем распечатайте повернутый символ
  • Если онесли нет, распечатайте символ как есть

Вы можете найти этот прохождение (из более ранней итерации курса) полезным.

0 голосов
/ 23 мая 2019

Этот код неверен:

int key = (int) argv[1];

argv - это массив string, который в CS50 - не более чем запутанный указатель char *.

За 5.1.2.2.1 Запуск программы стандарта C :

Функция, вызываемая при запуске программы, называется основной. Реализация не объявляет прототип для этой функции. Он должен быть определен с типом возврата int и без параметров:

    int main(void) { /* ... */ }

или с двумя параметрами (именуемыми здесь как argc и argv, хотя могут использоваться любые имена, поскольку они являются локальными для функции, в которой они объявлены):

    int main(int argc, char *argv[]) { /* ... */ }

или эквивалентный; ...

Итак, argv[1] - это значение указателя char *, которое вы затем присваиваете значению int. Это берет адрес некоторой памяти (скажем, значение в argv[1] равно 0xFF0403220020480C) и пытается вставить его в вероятные 4 байта переменной int key (которой в этом случае будет присвоено усеченное значение 0x0020480C.)

Это не то, что вы пытаетесь сделать.

(IMO, ваша проблема здесь является прекрасным примером того, почему обфускация CS50 char * с типом string является чрезвычайно плохой идеей. Вы просто не можете понять C без понимания указателей и NUL -определенного char строк доступны через указатель char *, и запутывание, которое делает string делает это сложнее.)

Если вы хотите преобразовать строку в числовое значение , вы, вероятно, захотите что-то вроде strtol() ( никогда не используйте atoi(), так как в нем нет проверки ошибок и его использование может вызывать неопределенное поведение ):

char firstCharNotConverted;

// set errno to zero as strtol()
errno = 0;
long key = strtol( argv[ 1 ], &firstCharNotConverted, 0 );

// if errno is now non-zero, the call to strtol() failed (per Linux man page)
// need to examine key and the contents of firstCharNotConverted
// to figure out why
if ( errno != 0 )
{
    ...
} 

Правильные заголовки опущены в качестве упражнения для тех, кто пытается использовать этот код; -)

Обратите внимание, что я использовал long для key, поскольку вы не можете выполнить полную и правильную проверку ошибок на strtol(), если приведете возвращаемое значение к int.

Проверка ошибок strtol() может быть несколько сложной, поскольку возвращаемое значение (и присвоенное key в вышеприведенном коде) может быть любым значением, и нет возможных значений, которые не являются допустимыми long значениями, которые strtol() может возвращаться для обозначения ошибки, поэтому для правильной проверки ошибок необходимо проверить значения как errno, так и firstCharNotConverted, чтобы правильно определить, произошла ли ошибка. Справочная страница Linux сообщает:

Поскольку strtol () может законно возвращать 0, LONG_MAX или LONG_MIN (LLONG_MAX или LLONG_MIN для strtoll ()) в случае успеха и неудачи, вызывающая программа должна установить errno на 0 до вызова, а затем определить, произошла ли ошибка, проверив, имеет ли errno ненулевое значение после звонка.

После этого вызова strtol() вам нужно проверить, является ли key равным LONG_MIN или LONG_MAX с errno равным ERANGE для недостаточного или переполнения, или key равно 0 вам нужно проверить содержимое firstCharNotConverted, чтобы определить причину сбоя преобразования. Обратите внимание, что если key равно нулю и firstCharNotConverted не равно argv[ 1 ], то входная строка была правильно преобразована из нуля.

Ваша реализация шифра Ceaser также неверна:

    for (int i = 0, n = strlen(ptext); i < n; i++)
    {
        printf("%c", (( ptext[i] + key ) % 26);
    } 
    printf("\n");

будет просто выводить символы со значениями от 0 до 25 - которые не являются буквами в наборе символов ASCII.

Здесь уже размещены многочисленные вопросы о шифрах Ceaser, поэтому я не собираюсь писать код. См. Код шифра Цезаря для одного примера вопроса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...