I'm going through CS50 and one of the problem sets was to implement a vigenere cipher. I have had some previous classroom experience with C before CS50, but still consider myself a beginner.
The program takes in a command line argument as a key (and requires it to be composed of only alphabetical characters) and then prompts the user to input a string for plaintext (the string that needs to be encrypted). The key is mapped out such that A or a is 0, B or b is 1, C or c is 2, ...., Z or z is 25. The program applies the cipher to plaintext only if the character in plaintext is alphabetical. If any character in plaintext is non-alphabetical, it just prints it out in its original form. The case of each letter in plaintext is also preserved.
#include <stdio.h>
#include <string.h>
#include <cs50.h>
#include <ctype.h>
//this function checks if the command line argument has any non-alphabetic characters
int valid_command(string cmd);
//this function converts the key to an array of ints that correspond to each character in the key (ex: A or a = 0, B or b = 1, etc.) and returns a pointer to that array
int* convert_key (string cmd);
//calculate the length of the string
int length (string cmd);
int main (int argc, string argv[]){
//cmd is used as a bool for the following if-statement- It's used to check if the command line argument (key) has any non-alphabetic characters
int cmd = valid_command(argv[1]);
//error checking for if the user input more than 2 command line arguments or if the key has any non-alphabetic characters
if ((argc != 2) || cmd){
printf("Error: Not valid input!\n");
return 1;
}
else{
printf("plaintext: ");
string plaintext = get_string();
int size = length(argv[1]); //save the length of the key
//convert key to an array of integers
int* delta = convert_key(argv[1]);
int* cipher = (int*)malloc((size+1)*sizeof(int));
int* cipher_ptr = cipher;
char* p_ptr = plaintext;
//go through each character in plaintext, determine if it's an alphabetic character and whether it's in the A-Z range or a-z range and use a calculation based on that to figure out how the character should be encrypted. This also takes into account wrapping of the key array (if the key array is smaller than plaintext, then once the last character in key is used to encrypt a character in plaintext, the next character should start over from the beginning of key) and also takes into account maintaining the case of the character of plaintext
if (plaintext != NULL){
int i = 0;
while ((*p_ptr) != '\0'){
//implement wrapping of key array
if (i == size){
i = 0;
}
if (isalpha(*p_ptr)){
//if element is in A-Z range:
if (isupper(*p_ptr)){
*cipher_ptr = ((((*p_ptr)-65)+(delta[i]))%26) + 65;
p_ptr++;
cipher_ptr++;
i++;
}
else {
*cipher_ptr = ((((*p_ptr)-97)+(delta[i]))%26) + 97;
p_ptr++;
cipher_ptr++;
i++;
}
}
else {
//if the character in plaintext is non-alphabetic, just copy that into the cipher array, but don't go on to the next element in key array
*cipher_ptr = *p_ptr;
p_ptr++;
cipher_ptr++;
}
}
}
else {
return 1;
}
//print out the encrypted ciphertext
printf("ciphertext: ");
//put into chars
char ciphertext [length(plaintext)];
for (int i = 0; i < length(plaintext); i++){
ciphertext[i] = *cipher;
cipher++;
printf("%c",ciphertext[i]);
}
printf("\n");
return 0;
}
}
//pass in the key string and return a pointer to array of integers that are integer representations of each character in key string
int* convert_key (string cmd){
char* ptr = cmd;
int* array = (int*) malloc(strlen(cmd)*sizeof(int));
int* array_ptr = array;
while ((*ptr) != '\0'){
*array_ptr = (toupper((*ptr))-65);
ptr++;
array_ptr++;
}
return array;
}
//this function checks if the command line argument has any non-alphabetic character
//return 0 if there are no non-alphabetic characters
//return 1 if there are non-alphabetic characters
int valid_command(string cmd){
char* ptr = cmd;
if (ptr != NULL){
while ((*ptr) != '\0'){
int value = (toupper((*ptr))-65);
if ((value > 25) || (value < 0)){
return 1;
}
else {
ptr++;
}
}
return 0;
}
else{
return 1; //return 1 if no command was given
}
}
int length (string cmd){
int length = 0;
char* ptr = cmd;
while ((*ptr) != '\0'){
length++;
ptr++;
}
return length;
}
This works, but it seems a bit cumbersome, I think it could be shortened and made to be more readable. If you see any mistakes that my own testing and the grader didn't catch, I would greatly appreciate if you pointed that out as well. Any tips on improving my approach or logic would be awesome as well.
string
withchar*
? What is the difference? Doesget_string
allocate storage? \$\endgroup\$