Substitution
Problem to Solve
A substitution cipher encrypts a message by replacing every letter with a different letter. The replacements are defined by a key that maps each letter of the alphabet to another letter. Anyone who wants to decrypt the message must know that key and reverse the mapping from ciphertext back to plaintext.
Consider this key:
NQXPOMAFTRHLZGECYJIUWSKDVB
The key has 26 characters. Its first character says that A becomes N; its
second says that B becomes Q; its third says that C becomes X; and the
remaining characters define the mappings through Z. Under this key, HELLO
is encrypted as FOLLE.
Create a file named substitution.c inside a folder named substitution.
Write a program that accepts the substitution key as a command-line argument,
asks the user for plaintext, and prints the resulting ciphertext.
Specification
Design and implement a program named substitution that encrypts messages with
a substitution cipher.
- Accept exactly one command-line argument: the substitution key.
- Treat the key itself as case-insensitive. Uppercase and lowercase versions of the same key must define the same mapping.
- If the program receives no key or more than one argument, print a clear error
message and return
1frommain. - A valid key contains exactly 26 alphabetic characters and includes every letter exactly once, ignoring case.
- If the key has the wrong length, contains a non-letter, or repeats a letter,
print a clear error message and return
1immediately. You may useUsage: ./substitution keyfor all invalid keys. - Prompt for the message by printing
plaintext:without first printing a newline. - Print
ciphertext:followed by the encrypted message. - Substitute alphabetic characters according to the key. Copy spaces, punctuation, digits, and other non-letter characters without changing them.
- Preserve the case of the plaintext: uppercase letters remain uppercase, and lowercase letters remain lowercase.
- Print one newline after the ciphertext and return
0frommain. - Use standard C and character arrays.
For this local version, you may assume that the plaintext contains no more than 254 characters.
Advice and Hints
The validity of the key and the substitution of the plaintext are separate problems. Implement and test them separately.
Validate the command-line arguments
A valid invocation has argc == 2, because argc also counts the program
name. The key is stored in argv[1].
Begin by testing the number of arguments. Then pass the key to a separate validation function:
int valid_key(const char key[]);
Check the key's length and characters
Use strlen, declared in string.h, to verify that the key has exactly 26
characters. Then visit each character and use isalpha from ctype.h to
verify that it is a letter.
Convert a char to unsigned char before passing it to a ctype.h
function.
Check for repeated letters
Use an array to remember which letters have already appeared:
int seen[26] = {0};
Convert each key character to one consistent case. Subtract 'A' to turn
it into an index from 0 to 25. If that position in seen is already 1,
the key repeats a letter and is invalid. Otherwise, set the position to 1.
Substitute one character
Add a function with this prototype:
char substitute(char c, const char key[]);
If c is uppercase, subtract 'A' to find its position in the alphabet;
if it is lowercase, subtract 'a'. Use that position as the index into the
key, then convert the selected key character to the original character's
case. Return non-letter characters unchanged.
Read a complete message
Use fgets with a 256-character array so that the plaintext may contain
spaces. Process characters until you reach the newline stored by fgets
or the null character, \0. Print exactly one newline after the
ciphertext.
Starter Code
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int valid_key(const char key[]);
char substitute(char c, const char key[]);
int main(int argc, char *argv[])
{
// Check the number of arguments and validate the key.
// Read the plaintext.
// Substitute and print each character.
}
int valid_key(const char key[])
{
// Check the key's length, characters, and uniqueness.
}
char substitute(char c, const char key[])
{
// Substitute letters, preserve case, and return other characters as-is.
}
How to Test
Compile and run the program from the substitution folder:
cc substitution.c -o substitution
./substitution NQXPOMAFTRHLZGECYJIUWSKDVB
If your system provides gcc instead of cc, run
gcc substitution.c -o substitution.
Test missing and invalid keys:
$ ./substitution
Usage: ./substitution key
$ ./substitution ABC
Usage: ./substitution key
$ ./substitution ABCDEFGHIJKLMNOPQRSTUVWXY1
Usage: ./substitution key
$ ./substitution AAAAAAAAAAAAAAAAAAAAAAAAAA
Usage: ./substitution key
Test a valid key:
$ ./substitution NQXPOMAFTRHLZGECYJIUWSKDVB
plaintext: HELLO
ciphertext: FOLLE
$ ./substitution NQXPOMAFTRHLZGECYJIUWSKDVB
plaintext: Hello, World!
ciphertext: Folle, Kejlp!
Also test a lowercase key, mixed-case plaintext, punctuation, digits, and a message containing spaces.
Return to the Problem Set 2 overview.