Задания второй недели курса CS50 (Caesar, Vigenere)

4.7 (94.29%) 7 vote[s]

Цезарь шифровал все конфиденциальные сообщения путем смещения каждой буквы на несколько позиций. Он мог написать A как B, B как C, C как D … Z как A. Чтобы сказать HELLO, Цезарь мог написать IFMMP. Получатель мог прочитать сообщение, сместив текст на определенное число позиций в алфавите. Это число и есть код шифрования Caesar, который предлагается реализовать.

Caesar

Задача состоит в том, чтобы реализовать код, который запрашивает у пользователя число, затем текст, и выводил зашифрованное сообщение.

В этом шифре пользователь вводит число, которое сообщит нам, на сколько мест сдвинуть букву в алфавите.

Сначала нужно убедиться, что пользователь вводит корректные данные. Вводимое число мы объявляем в виде однорядного строчного массива string argv[].Это должен быть один аргумент в виде целого числа. Для этого используем оператор if. Если в введенных данных нет двух аргументов (if(argc != 2)), или элементы массива не являются числами (if (! isdigit(argv[1][j]))), мы возвращаемся к началу и повторяем запрос. return 1 дает знать программе, что эти данные неприемлемы.

int argc сообщает, сколько существует аргументов командной строки, а argv выдаст фактическую строку, которую набрал пользователь. Затем мы при помощи функции atoi из библиотеки stdlib.h преобразуем строку в целое число. Последний оператор  if проверяет, не является ли полученное число отрицательным, и в случае подтверждения возвращаемся к повторному запросу («Usage: ./caesar key\n»).

Шифрование текста производится при помощи цикла for. Цикл будет выполняться от 0 до длины текста, введенного пользователем. Обратите внимание, что у нас есть оператор if, else if, else, который будет управлять вводом для шифрования. Сохранение регистра букв мы выполняем при помощи функций isupper и islower из библиотеки ctype.h.

Псевдокод (или алгоритм) такой:

Поочередно к каждому символу  добавляем нужное для перемещения количество пробелов.

Затем мы вычтем 97 из этого числа. Это даст нам номер буквы в алфавите из 26 символов.

В ASCII-таблице строчные цифры начинаются с 97, а верхний регистр начинается с 65. Поэтому нам нужно отрегулировать это, чтобы привести их в наш 26-буквенный алфавит.

Далее, мы используем некоторую модульную арифметику, чтобы убедиться, что наш шифр способен оборачиваться. Если пользователь введет 27, мы полностью вернемся и сместим букву только на один пробел.

Добавляем 97 (или 65 в верхнем регистре) к символу, чтобы дать правильное представление ASCII.

#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
  
int main(int argc, string argv[])
{ 
    bool Success = false;
    int key = 0;
    int input_length = 0;
    string text = "";
    
    do
    {
        if(argc != 2)
        {
            printf("Usage: ./caesar key\n");
            return 1;
        }
        for (int j = 0, n = strlen(argv[1]); j < n; j++)
    {
        if (! isdigit(argv[1][j]))
        {
            printf("Usage: ./caesar key\n");
         return 1;
        }
    }     
       key = atoi(argv[1]);   
    if (key < 0)
    {
        printf("Usage: ./caesar key\n");
        return 1;
    }
        else
        {
            Success = true;
            printf("plaintext:  ");
        }
   } 
    while(!Success);
    text = get_string("");
     printf("ciphertext: ");
       input_length = strlen(text);
       for(int i = 0; i < input_length; i++)
    { 
        if(isalpha(text[i]))
        {
           if(islower(text[i]))
            {   
                printf("%c", ((((text[i] - 97)+key)%26)+97));
            }
           else
            {   
              printf("%c", ((((text[i] - 65)+key)%26)+65));
            }
        }
       else
        {   
          printf("%c", text[i]);
        }
    }
   printf("\n");
    return 0;  
}

Вот какие варианты вывода должна демонстрировать программа при корректном вводе данных: 

$ ./caesar 1
 plaintext:  HELLO
 ciphertext: IFMMP
 
$ ./caesar 13
 plaintext:  hello, world
 ciphertext: uryyb, jbeyq
 
$ ./caesar 13
 plaintext:  be sure to drink your Ovaltine
 ciphertext: or fher gb qevax lbhe Binygvar

При некорректных данных:
 
$ ./caesar HELLO
 Usage: ./caesar key
 
$ ./caesar 1 2 3
 Usage: ./caesar key

Народная мудрость

Vigenere

Шифр Віженера — это усовершенствованный вариант шифра Цезаря. Шифрование осуществляется путем использования последовательности ключей ( ключевого слова).

Если  p – это обычный текст, k – ключевое слово (например, алфавитный ряд, где А (или а) представляет 0, B (или b) представляет 1, C (или c) представляет 2, Z — 25), то каждая буква ci, в зашифрованном тексте c высчитывается как:

ci = (pi + kj) % 26

При этом шифр использует kj против k. Если длина k меньше длины p, то буквы в k должны быть циклично использованы столько раз, сколько необходимо для шифрования p.

Пример шифра
Пример шифра

В отличие от реализации шифра Цезаря, в этом примере нужно убедиться, что все вводимые данные являются буквами. Для этого также используется цикл for для перечисления через строку, чтобы убедиться, что каждый символ является альфа (if (!isalpha(argv[1][i]))).

В отличие от кода шифрования Caesar, здесь добавлены несколько строк для isupper и islower. Если текст, который нужно зашифровать, длиннее ключа, то при помощи модальной арифметики мы возвращаемся назад. Дальше мы устанавливаем k, которое равно порядковому номеру текущего символа в алфавите (вычитаем 97 из номера символа). «а» будет перемещать текст на один символ, а «с» будет перемещать текст на три символа.

Последняя строка увеличивает переменную J, так что каждый раз, когда мы проходим через цикл, мы используем следующий символ в ключевом слове.

Помним, что %c — это формат печати символа.

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

int main(int argc, string argv[])
{    
    if (argc != 2)
    {
        printf("Usage: ./vigenere keyword\n");
        return 1;
    }
    else 
    {
        for (int i = 0, n = strlen(argv[1]); i < n; i++)
        {
            if (!isalpha(argv[1][i]))
            {
                printf("Usage: ./vigenere keyword\n");
                return 1;
            }    
        }
    }
       printf("plaintext:  ");
   
    string k = argv[1];
    int kLen = strlen(k);
    string p = get_string("");
       printf("ciphertext: ");
    for (int i = 0, j = 0, n = strlen(p); i < n; i++)
    {            
        int letterKey = tolower(k[j % kLen]) - 'a';
        if (isupper(p[i]))
        {
            printf("%c", 'A' + (p[i] - 'A' + letterKey) % 26);
           j++;
        }
        else if (islower(p[i]))
        {
            printf("%c", 'a' + (p[i] - 'a' + letterKey) % 26);
            j++;
        }
        else
        {
           printf("%c", p[i]);
        }
    }
      printf("\n");
    return 0;
}

Вот как работает программа при корректном вводе данных:
 
$ ./vigenere bacon  
plaintext: Meet me at the park at eleven am
ciphertext: Negh zf av huf pcfx bt gzrwep oz

$ ./vigenere A
plaintext: hello
ciphertext: hello

$ ./vigenere b
plaintext: HELLO
ciphertext: IFMMP

$ ./vigenere C
plaintext: HeLlO
ciphertext: JgNnQ

При некорректном вводе:

$ ./vigenere 123
Usage: ./vigenere keyword

$ ./vigenere bacon and eggs
Usage: ./vigenere keyword

Подписаться
Уведомление о
guest
0 комментариев
Inline Feedbacks
View all comments
Просмотры: 2325

Популярные записи