We continue to do homework from Problem set 2 in Computer science CS50 (Harvard). Last time, we learned how to encrypt text by shifting letters by a certain number (key) entered by the user. And this time we will solve the problem more complicated, it will be the Substitution.c program and it also encrypts the text by replacing some letters with others according to the principle that the user enters on the command line when the program starts. This principle looks like a string of letters of the alphabet arranged in a reversed order, as in the example:
$ ./substitution JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext: HELLO
ciphertext: VKXXN
For example, the letter “H” should be replaced by the 8th letter from the entered string
JT R E KY A V OGDXPSNCUIZLFBMWHQ
1 2 3 4 5 6 7 8 …
— it’s “V”, because if you count in the usual alphabet ABCDEFGH…, then “H”— is the eighth letter.
A few points that might come in handy:
- at the beginning of the program, after checking for input errors, you can make an array that will contain the indices of all 26 letters entered by the user, this array is convenient to use later to replace letters;
- to check if all 26 letters are unique, it is convenient to introduce a variable to count the number of each letter in the input array.
Igroglaz solution:
#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++; }
Solution by Shtukensia:
#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'; } } }