Продолжаем выполнять домашние задания из 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';
}
}
}
