From problem set 2
This time we’re preparing to code a substitution cipher. Instead of getting a number for key, we’ll be getting a string. A 26 character long string to be more exact, where each character will replace it’s equivalent index on the alphabet.
“A key, for example, might be the string
NQXPOMAFTRHLZGECYJIUWSKDVB. This 26-character key means that
A (the first letter of the alphabet) should be converted into
N (the first character of the key),
B (the second letter of the alphabet) should be converted into
Q (the second character of the key), and so forth.”
$ ./substitution YTNSHKVEFXRBAUQZCLWDMIPGJO
Acquiring and validating the key should follow basically the same steps we saw in Caesar, with some tests needing a little more thinking through. Encrypting should be as easy or easier.
Some instructions before proceeding include that we should only modify alphabetical characters and that if the case is lower or upper doesn’t matter for the key, but the text must remain at the same case it was given. Any time you use return to terminate it must return 1. Remember, the key must be 26 characters long with no repetitions and alphabetical only.
// Validate the key
// Check argc for 2 and argv for len and non-alpha chars or repetitions
// Ask user for plain text and store it
// Replace each char in text with the char with same index in key. Keep case and non-alpha
Seems simple enough, the core work here is making the code check check and check for every little thing. After some thinking, base code ended up like this:
// Ciphers a text using substitution cipher#include <stdio.h>
#include <string.h>// Prototype functions
void subs(char *, char*);int main(int argc, char *argv)
//validate key here
string text = get_string("Enter Text: ");
}void subs(char *t, char *k)
As I did in Caesar, I’m checking for argc and argv inside main function. I included ctype and string along with the other usual libraries. We have the proto for our only custom function and the get plaintext part is pretty much done. So we proceed to validate the key.
int main(int argc, char *argv)
//validate key here
if (argc != 2)
printf("Usage: ./substitution key\n");
if (strlen(argv ) != 26)
printf("Key must contain 26 characters.\n");
for (int i = 0; i < strlen(argv); i++)
if (isalpha(argv[i]) == 0)
printf("Key must only contain alphabetic characters. \n");
for (int r = i + 1; r < strlen(argv); r++)
if (toupper(argv[r]) == toupper(argv[i]))
printf("Key must only contain one of each alphabetic character. \n");
First the code checks if argc is correct with 2 parameters (./substitution key) if not we explain the error and exit. Ok on that, we check for the key lenght, again if it’s not 26 we explain and exit. Now we need to check if every character inside the key is a alphabetic character, for this a loop iterates through the string checking with isalpha every character. Instructions also warned us about repetition, to check that we need a nested loop. With the nested loop we can keep the value from de first loop and compare it against every other character using value + 1 inside the nested loop. Paying attention that our key is not case sensitive we need to make sure both letter are upper or lower case, I chose upper for no particular reason, it’s easier to use toupper for both letters than to discover the case one value is using and copying it to the other. To end our key checks we return a error if we find a duplicated character, and exit.
From here we can test if our code is checking keys correctly. Grab a example from problem set and try to make it fail.
~/pset2/substitution/ $ ./substitution VcHpRzGjNtLsKfBdQwAxEuYmOi
~/pset2/substitution/ $ ./substitution VcHpRzGjNtLsKfBdQwAxEuYmOiL
Key must contain 26 characters.
~/pset2/substitution/ $ ./substitution VcHpRzGjNtLsKfBdQwAxEuYmO5
Key must only contain alphabetic characters.
~/pset2/substitution/ $ ./substitution VcHpRzGjNtLsKfBdQwAxEuYmOv
Key must only contain one of each alphabetic character.
~/pset2/substitution/ $ ./substitution VcHpRzGjNtLsKfBdQwAxEuYmOi o
Usage: ./substitution key
Now that our key is working correctly for sure, we can proceed to encrypting the text. Strings t and k go for text and key respectively.
void subs(char *t, char *k)
for (int i = 0; i < strlen(t); i++)
if (isalpha(t[i]) != 0)
if (isupper(t[i]) != 0)
int index = t[i] - 'A';
int index = t[i] - 'a';
The function begins printing “ciphertext”, no breaking line. Then a loop starts with value 0 to length of text. It first checks if the character in t is alphabetic, then if it’s upper or lowercase. Inside the ifs checking the case, we use a formula very much like Caesars to get the alphabetical index of t. Then it’s just a matter of printing that same index, but from the key string instead of text string. We do it for each single character on the text, one at a time. On the other hand, if it’s not alphabetical, we just print it as it is. To end the function and the program, just print a new line.
Staff tests and full code with comments bellow.
Results generated by style50 v2.7.4
Results for cs50/problems/2020/x/substitution generated by check50 v3.1.2
:) substitution.c exists
:) substitution.c compiles
:) encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
:) encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
:) encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
:) encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
:) encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
:) encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
:) encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
:) encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
:) handles lack of key
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key