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';
}
}
}