Язык Си: продвинутое шифрование по-цезарски


Продолжаем выполнять домашние задания из Problem set 2 в курсе Computer science CS50 (Harvard). В прошлый раз мы научились шифровать текст сдвигом букв на некое число (ключ), введенный пользователем. А в этот раз решим задачу посложнее, это будет программа Substitution.c и в ней тоже происходит шифрование текста путем замены одних букв на другие по принципу, который вводит пользователь в командной строке при запуске программы. Этот принцип выглядит как строка букв алфавита, расположенных в измененном порядке, так в примере:

$ ./substitution JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext:  HELLO
ciphertext: VKXXN

К примеру, буква «H» должна замениться на 8-ую букву из введенной строки
JT R E KY A V OGDXPSNCUIZLFBMWHQ
1 2 3 4 5 6 7 8

— это «V», потому что если считать по обычному алфавиту ABCDEFGH…, то «H» — это восьмая буква.

Несколько моментов, которые могут пригодиться:

  • в начале программы после проверок на ошибки ввода можно сделать массив, который будет содержать индексы всех 26 букв, введенных пользователем, этот массив удобно потом использовать для замены букв;
  • чтобы проверить являются ли все 26 букв уникальными, удобно ввести переменную для подсчета количества каждой буквы в введенном массиве.

Решение (Игроглаз):

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

void build_index(char *AZ);
int main(int argc, char *argv[])
{
    int i, n, j, x;
    char text[1000]; // text input
    char cypher[1000]; // cypher output
    char AZ[26]; // alphabetical indexes

    /*** STEP 1 - check argv ***/

    // in case if user didn't input proper console argument
    if (!(argc == 2) || (strlen(argv[1]) != 26))
    {
        printf("Usage: ./substitution key");
        return 1;
    }

    // build A-Z and a-z indexes
    build_index(AZ);

    // check console argument for wrong or non-unique characters
    for (i = 0; i < 26; i++)
    {
        // capitalise letters
        if (argv[1][i] >= 'a' && argv[1][i] <= 'z')
            argv[1][i] = toupper(argv[1][i]);

        // take capital letter and compare it with each letter in index
        if (argv[1][i] >= 'A' && argv[1][i] <= 'Z')
        {
            for (j = 0; j < 26; j++)
            {
                // if letter found in indexes - make this index variable 0
                if (argv[1][i] == AZ[j])
                    AZ[j] = 0;
            }
        }
        else
        {
            printf("Error: entered wrong symbols as a key");
            return 1;
        }
    }

    // finally argv processing step: summ all indexes
    for (i = 0, j = 0; j < 26; j++)
        i += (int)AZ[j];
    // summ should be 0
    if (i > 0)
    {
        printf("Error: key must consist out of unique characters");
        return 1;
    }

    /*** STEP 2 - get phrase ***/

    // now lets get phrase to cipher
    printf("plaintext: ");

    for (i = 0, n = 0; ; i++)
    {
        scanf ("%c", &text[i]);        // get user input
        if (text[i] == '\n')
        {
            printf("\n");
            break;    // end input when user press 'Enter'
        }
        n++; // count number of input
    }

    /*** STEP 3 - sipher text ***/

    // now lets get phrase to cipher
    printf("ciphertext: ");

    // replace plaintext with cipher
    for (i = 0; i <= n; i++)
    {
        if (text[i] >= 'A' && text[i] <= 'Z')
        {
            x = (int)text[i] - 65;
            printf("%c", argv[1][x]);
        }
        else if (text[i] >= 'a' && text[i] <= 'z')
        {
            x = (int)text[i] - 97;
            printf("%c", tolower(argv[1][x]));
        }
        else
            printf("%c", text[i]);
    }

    getchar();
    getchar();
    return 0;
}

void build_index(char *AZ)
{
    int i, j;
    for (i = 0, j = 65; i < 26; i++) // note: AZ array elements are 0-25
        AZ[i] = j++;
}

Решение (Штукенция):

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

char rotate(char c, int n, int keys[]);

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

// Check if user write key as string argv[]:
    if (argc != 2)
    {
        printf("Usage: ./substitution key\n");
        return 1;
    }

// Check if all key 26 characters of argv[] are alphabetical:
    int k = 1;
    int i = 0;
    int sum_of_key = 0;
    int key_for_letter = 0; // key number for changing character
    int keys [26]; // array for all key numbers for changing characters

    for (i = 0; argv[k][i]; i++)
    {
        int k_check = isalpha(argv[k][i]);
        if (k_check == 0)
        {
            printf("Key must contain characters.\n");
            return 1;
        }
        else if (k_check != 0)
        {
            int check_key = isupper(argv[k][i]); // check if key char is uppercase
            if (check_key > 0)
            {
                int letter_id = (argv[k][i]);
                key_for_letter = letter_id - 65;
                keys[i] = key_for_letter;
            }
            if (check_key == 0)
            {
                int letter_id = (argv[k][i]);
                key_for_letter = letter_id - 97;
                keys[i] = key_for_letter;
            }
            sum_of_key += 1;
        }
    }

// Check if lenth of key is OK

    if (sum_of_key != 26)
    {
        printf("Key must contain 26 characters.\n");
        return 1;
    }

// Check if each character of argv[] key is unique:

    for (int q = 0; keys[q]; q++)
    {
        for (int z = (q + 1); z <= (25 - q); z++)
        {
            if (keys[q] == keys[z])
            {
                printf("Key must contain 26 UNIQUE characters.\n");
                return 1;
            }
        }
    }


// Get user input of plaintxtx:
    printf("plaintext:");
    string plaintxtx = get_string("  ");
    int textlen = strlen(plaintxtx);


// Turn characters from plaintxtx to ciphertext with function
    char c; // character from plaintxtx
    char c_new; // newmade rotated character
    int n = 0; // index for shifting letter

    printf("ciphertext: ");

    int j = 0;
    for (j = 0; j < textlen; j++) // print char by char rotated letters
    {
        c = plaintxtx[j];
        c_new = rotate(c, n, keys);
        printf("%c", c_new);

    }

    printf("\n");
    return 0;
}

char rotate(char c, int n, int keys[])
{
    int check_c = isalpha(c); // check if char is letter

    if (check_c == 0) // if char is not letter, return as it is
    {
        return c;
    }

    else
    {
        char c_new = ' '; // rotated newmade character
        int check_upper = isupper(c); // check if char is uppercase

        if (check_upper > 0)
        {

            int c_asci = (c); // convert char LETTER to int ASCII number (A->65)
            int num_letter = (c_asci - 65); // find numer of upper letter in alphabet (index)
            int shifted_index = keys[num_letter]; // shift index of letter with key number
            c_new = shifted_index + 65; // convert ASCII number back to new letter

            return c_new;
        }

        else if (check_upper == 0)
        {
            int c_asci = (c); // convert char LETTER to int ASCII number (a->97)
            int num_letter = (c_asci - 97); // find numer of lower letter in alphabet (index)
            int shifted_index = keys[num_letter]; // shift index of letter with key number
            c_new = shifted_index + 97; // convert ASCII number back to new letter
            return c_new;
        }

        else
        {
            return 'N';
        }
    }
}

Запись опубликована в рубрике С (Си). Добавьте в закладки постоянную ссылку.

Добавить комментарий

🇬🇧 Attention! Comments with URLs/email are not allowed.
🇷🇺 Комментарии со ссылками/email удаляются автоматически.