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