Язык Си: шифрование текста Цезаря


Продолжаем делать домашние задания по курсу Computer science CS50 (Harvard), и в этот раз нужно написать код программы и зашифровать текстовое послание.

Суть задания ceasar.c заключается в том, чтобы программа запрашивала ввод текста пользователя, а затем выводила ниже строку текста в зашифрованном виде с ключом пользователя. Ключ же указывается при запуске программы в командной строке после называния самой программы argv[]. Так в примере запуска готовой программы пользователь вводит ключ = 13 и свой текст «Hello, world!», а на выходе получает этот текст в виде шифра:

Важно, чтобы в программе соблюдались следующие условия:

  • если пользователь вводит заглавные буквы, то в шифре тоже появляются заглавные, если строчные — то строчные;
  • если пользователь вводит что-то, кроме букв, то эти символы печатаются в исходом виде;
  • ключ для шифрования, который пользователь передает программе при запуске через командную строку, может быть положительным числом, это число не обязательно соответствует количеству букв в алфавите, а может быть и больше, например, 100 — в этом случае исходная буква заменяется не на +100 символ ASCII, а на букву в промежутке от a до z или от A до Z, отстоящую от исходной на 100 шагов вперед;
  • формула для расчета индекса сдвига букв при шифровании:
    Ci=(Pi+key)%26,
    где Ci — зашифрованная буква, Pi — исходная буква, key — число ключ шифрования.

Решение Игроглаза #1. Решение получилось несколько избыточным в плане дополнительной кодировки символов; с другой стороны оно может пригодится в следующем задании.

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

int only_digits(char *argv[]);

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

    // in case if user didn't input any console argument
    if (!(argc == 2))
    {
        printf("Usage: ./caesar key");
        return 1;
    }

    // check console argument (char) and convert them to (int)
    key = only_digits(argv);
    if (key < 0)
    {
        printf("Usage: ./caesar key");
        return 2;
    }

    printf("plaintext: ");

    for (n = 0; ; n++) // we also count number of input for the next cycle
    {
        scanf("%c", &text[n]); // input
        if (text[n] == '\n')   // if input ended - put new line
            break;
    }

    // build A-Z and a-z indexes
    for (i = 0, j = 65; i < 26; i++) // note: AZ array elements are 0-25
        AZ[i] = j++;
    for (i = 0, j = 97; i < 26; i++)
        az[i] = j++;

    printf("ciphertext: ");

    for (i = 0; i < n; i++)
    {
        if (text[i] >= 'A' && text[i] <= 'Z') // print capital
        {
            text[i] -= 65; // to move ASCII to 0
            text[i] = (text[i] + key)%26; // key formula to move past 'z'
            x = (int)text[i]; // convert char to int
            printf("%c", AZ[x]); // print based on index
        }
        else if (text[i] >= 'a' && text[i] <= 'z') // print small
        {
            text[i] -= 97;
            text[i] = (text[i] + key)%26;
            x = (int)text[i];
            printf("%c", az[x]);
        }
        else
            printf("%c", text[i]); // print rest characters
        if (text[i] == '\n')
            break;
    }

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

int only_digits(char **argv)
{
    unsigned int i;
    unsigned int len = strlen(argv[1]);

    for (i = 0; i < len; i++)
    {
        if (argv[1][i] < '0' || argv[1][i] > '9')
            return -1;
        else
        // convert argv 'number' characters to real number
            return strtol(argv[1], NULL, 10);
    }

    // Error: no argv was processed
    return -2;
}

Первый вариант был корректным, но не прошел тестирование cs50. Вот исправленный вариант #2 от Игроглаза:

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

int only_digits(char *argv[]);

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

    // in case if user didn't input any console argument
    if (!(argc == 2))
    {
        printf("Usage: ./caesar key");
        return 1;
    }

    // check console argument (char) and convert them to (int)
    if (only_digits(argv) == 1)
    {
        printf("Usage: ./caesar key");
        return 1;
    }

    // convert argv 'number' characters to real number
    key = strtol(argv[1], NULL, 10);

    if (key < 0)
    {
        printf("Usage: ./caesar key");
        return 1;
    }

    printf("plaintext: ");

    for (n = 0; ; n++) // we also count number of input for the next cycle
    {
        scanf("%c", &text[n]); // input
        if (text[n] == '\n')   // if input ended - put new line
        {
            break;
        }
    }

    // build A-Z and a-z indexes
    for (i = 0, j = 65; i < 26; i++) // note: AZ array elements are 0-25
    {
        AZ[i] = j++;
    }
    for (i = 0, j = 97; i < 26; i++)
    {
        az[i] = j++;
    }

    printf("ciphertext: ");

    for (i = 0; i < n; i++)
    {
        if (text[i] >= 'A' && text[i] <= 'Z') // print capital
        {
            text[i] -= 65; // to move ASCII to 0
            text[i] = (text[i] + key) % 26; // key formula to move past 'z'
            x = (int)text[i]; // convert char to int
            printf("%c", AZ[x]); // print based on index
        }
        else if (text[i] >= 'a' && text[i] <= 'z') // print small
        {
            text[i] -= 97;
            text[i] = (text[i] + key) % 26;
            x = (int)text[i];
            printf("%c", az[x]);
        }
        else
        {
            printf("%c", text[i]); // print rest characters
        }
        if (text[i + 1] == '\n')
        {
            printf("\n");
            break;
        }
    }

    return 0;
}

int only_digits(char **argv)
{
    unsigned int i;
    unsigned int len = strlen(argv[1]);

    for (i = 0; i < len; i++)
    {
        if (!isdigit(argv[1][i]))
        {
            return 1;
        }
    }
    return 0;
}

 

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

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

char rotate (char c, int n);

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

// Check if user write key number correctly as string argv[]:
    if (argc!=2)
    {
        printf("Error: command-line arguments\n");
        return 1;
    }
    int k=1;
    int i=0;
    int key=0;
    for (i=0; argv[k][i];i++)
    {
        int k_check = isdigit(argv[k][i]);
        if (k_check==0)
        {
            printf("Usage: ./caesar key\n");
            return 1;
        }
    }

// Turn our key number string argv[1] to integer key_number:
    char* endPtr;
    int key_number = strtol(argv[1],&endPtr,10);

// Get use 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 = key_number; // key that user made

    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);
        printf("%c", c_new);
    }

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

char rotate (char c, int n)
{
    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=(num_letter+n)%26; // 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=(num_letter+n)%26; // 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';
    }

}

И после тестирования багов мой выглядит так:

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

char rotate(char c, int n);

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

// Check if user write key number correctly as string argv[]:
    if (argc != 2)
    {
        printf("Error: command-line arguments\n");
        return 1;
    }
    int k = 1;
    int i = 0;
    int key = 0;
    for (i = 0; argv[k][i]; i++)
    {
        int k_check = isdigit(argv[k][i]);
        if (k_check == 0)
        {
            printf("Usage: ./caesar key\n");
            return 1;
        }
    }

// Turn our key number string argv[1] to integer key_number:
    char *endPtr;
    int key_number = strtol(argv[1], &endPtr, 10);

// Get use 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 = key_number; // key that user made

    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);
        printf("%c", c_new);
    }

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

char rotate(char c, int n)
{
    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 = (num_letter + n) % 26; // 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 = (num_letter + n) % 26; // 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 удаляются автоматически.