CS50 - pset2 - substitution - c

I can't see what my code for pset2 substitution is missing. When I use check50 to test the program it returns this result:
:) substitution.c exists
:) substitution.c compiles
:( encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
expected "ciphertext: Z...", not "ciphertext: Z..."
:( encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
expected "ciphertext: z...", not "ciphertext: z..."
:( encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
expected "ciphertext: NJ...", not "ciphertext: NJ..."
:( encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
expected "ciphertext: Ke...", not "ciphertext: Ke..."
:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
expected "ciphertext: Cb...", not "ciphertext: Cb..."
:( encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
expected "ciphertext: Cb...", not "ciphertext: Cb..."
:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
expected "ciphertext: Cb...", not "ciphertext: Cb..."
:( encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
expected "ciphertext: Rq...", not "ciphertext: Rq..."
:) handles lack of key
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key
However, when I manually type in a key and plaintext it works exactly as it is supposed to. Furthermore, the 'expected' result of check50 seems to be exactly the same as the output so it is not obvious what is wrong.
my code is as follows:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int get_validkey(string A);
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("useage: ./substitution key\n");
return 1;
}
int validation = get_validkey(argv[1]);
if (validation == 1)
{
printf("key must contain 26 alphabetical characters\n");
return 1;
}
// prompting user for plaintext
string plaintext = get_string("plaintext: ");
printf("ciphertext: ");
int length = strlen(plaintext);
for (int c = 0; c <= length ; c++)
{
// printing any non-alphabet characters unchanged
if (plaintext[c] < 'A' || (plaintext[c] > 'Z' && plaintext[c] < 'a') || plaintext[c] > 'z')
{
printf("%c", plaintext[c]);
}
else
{
for (int b = 0; b <= 25; b++)
{
if (plaintext[c] == 65 + b)
{
char upper = argv[1][b];
int up = isupper(upper);
if (up == 0)
{
upper = toupper(upper);
printf("%c", upper);
}
if (up != 0)
{
printf("%c", upper);
}
}
else if (plaintext[c] == 97 + b)
{
char lower = argv[1][b];
int low = islower(lower);
if (low == 0)
{
lower = tolower(lower);
printf("%c", lower);
}
if (low != 0)
{
printf("%c", lower);
}
}
}
}
}
printf("\n");
return 0;
}
// function assesses if the key input is valid and returns 0 if it is and 1 if it is not
int get_validkey(string A)
{
int inputlength = strlen(A);
if (inputlength != 26)
{
return 1;
}
else
{
for (int g = 0; g < 26; g++)
{
// checks if the character is non alphabetical
if (A[g] < 'A' || (A[g] > 'Z' && A[g] < 'a') || A[g] > 'z')
{
return 1;
}
// scans all characters before A[g] to see if it has already been used
for (int k = (g - 1); k >= 0; k--)
{
if (A[k] == A[g])
{
return 1;
}
// also checks if different case of the same character has been used
if (A[k] == A[g] + 32)
{
return 1;
}
if (A[k] == A[g] - 32)
{
return 1;
}
}
// scans all characters after A[g] to check if it has been used already. (Not sure if this is necessary)
for (int l = (g + 1); l < 26; l++)
{
if (A[l] == A[g])
{
return 1;
}
// also checks if a different case of the same letter is used
if (A[l] == A[g] + 32)
{
return 1;
}
if (A[l] == A[g] - 32)
{
return 1;
}
}
}
return 0;
}
}
I am very new to programming and any help would be greatly appreciated.

I've encountered the same problem, but then I realized that when I was iterating the for loop for my plain text which is, for (int c = 0; c <= length ; c++), that the programming language itself internally starts with 0 when counting. Even though we knew it should be equal to the length of the plain text, the language will iterate to that null element within the array of string. I suggest you do this, for (int c = 0; c <= length ; c++). Hope, it helps.

Related

My CS50 caesar program works fine but it doesn't pass check50

This program is supposed to encrypt strings with the user's key. If the argument count is not 2 and the argv[1] (key) is not a positive number then it prints the usage instructions and returns 1.Else it does the rest of the program.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
//Get the key
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
for (int key = 0; key < strlen(argv[1]); key++)
{
if(isalpha(argv[1][key]))
{
printf("Usage: ./caesar key\n");
return 1;
}
}
//Convert string to int
int key = atoi(argv[1]);
//Get the plaintext
string plaintext = get_string("Plaintext: ");
//Output the ciphertext
printf("ciphertext: ");
for (int i = 0, length = strlen(plaintext); i < length; i++)
{
if(!isalpha(plaintext[i]))
{
printf("%c", plaintext[i]);
continue;
}
int offset = isupper(plaintext[i]);
for (int j = 0; plaintext[i] != '\0'; i++)
{
if (isalpha(plaintext[i]) != 0)
{
if (isupper(plaintext[i]) != 0)
{
printf("%c", ((plaintext[i] - 65 + key) % 26) + 65);
}
else
{
printf("%c", ((plaintext[i] - 97 + key) % 26) + 97);
}
}
else
{
printf("%c", plaintext[i]);
}
}
int p = plaintext[i] - offset;
int c = (p + key) % 26;
printf("%c", c + offset);
}
printf("\n");
return 0;
}
These are the check50 results:
:) caesar.c exists.
:) caesar.c compiles.
:( encrypts "a" as "b" using 1 as key
expected "ciphertext: b\...", not "ciphertext: b\..."
:( encrypts "barfoo" as "yxocll" using 23 as key
expected "ciphertext: yx...", not "ciphertext: yx..."
:( encrypts "BARFOO" as "EDUIRR" using 3 as key
output not valid ASCII text
:( encrypts "BaRFoo" as "FeVJss" using 4 as key
output not valid ASCII text
:( encrypts "barfoo" as "onesbb" using 65 as key
expected "ciphertext: on...", not "ciphertext: on..."
:( encrypts "world, say hello!" as "iadxp, emk tqxxa!" using 12 as key
expected "ciphertext: ia...", not "ciphertext: ia..."
:) handles lack of argv[1]
:) handles non-numeric key
:) handles too many arguments
But I can't figure out where's the problem. It literally says
-expected "ciphertext: b...", not "ciphertext: b..."
They seem completely same to me.
Edit:
I've changed 65 and 97 to 'a' and 'A'.That solved the ASCII value problem.
I also removed this line.But the problems still remain.
for (int j = 0; plaintext[i] != '\0'; i++)
I've figured out everything and it passed the check50 tests.Thank you for all the help.My solution is below the comments.
I've noticed that things get complicated after the second for loop and I removed everything
int offset = isupper(plaintext[i]);
to
printf("%c", c + offset);
And then I created a new function called mix_text and add that function with a %s in the line of printing ciphertext.This new function takes plaintext and key as its inputs and returns a string.Here's how it looks.
string mix_text(string plaintext, int key)
{
string ciphertext = plaintext;
for (int i = 0, len = strlen(plaintext); i < len; i++)
{
if (isupper(plaintext[i]))
{
ciphertext[i] = (plaintext[i] - 'A' + key) % 26 + 'A';
}
else if (islower(plaintext[i]))
{
ciphertext[i] = (plaintext[i] - 'a' + key) % 26 + 'a';
}
}
return ciphertext;

CS50 - pset2 - Substitution - "output not valid ASCII text"

My program is producing what is seems like the correct output but i still get the :( message when i run check50. I have already read other awnsers to similar questions but none of them seems actually similar to my problem.
check50 output:
:) 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
output not valid ASCII text
->:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
output not valid ASCII text
:) encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
:) does not encrypt non-alphabetical characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
:) handles lack of key
:) handles too many arguments
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key
I put the -> before the error messages for easier visualization
It is weird because right before the two errors there is an almost identical input/output that has been checked as correct
Here is my code:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
string subs(string plain, string key);
int key_check(string key);
int main(int argc, string argv[]) {
// CHECK IF IT HAS AN INPUT
if (argc < 2) {
printf("Usage: ./substitution key\n");
return 1;
}
// CHECK IF IT HAS MORE THAN 1 INPUT
if (argc > 2) {
printf("Usage: ./substitution key\n");
return 1;
}
// IF KEYCHECK FUNCTION DETECTS AN ERROR, RETURN 1
if (key_check(argv[1]) == 1) {
return 1;
}
// ELSE KEY = USER ARGV INPUT
string key = argv[1];
// GET USER PLAINTEXT INPUT
string plain = get_string("plaintext: ");
string cipher = subs(plain, key);
// PRINT RESULT
printf("ciphertext: %s\n", cipher);
}
int key_check(string key) {
// STRING LENGHT
int leng = strlen(key);
// CHECK IF KEY HAVE 26 CHARACTERS
if (leng < 26) {
printf("Key must contain 26 characters.\n");
return 1;
}
for (int i = 0; i < leng; i++) {
// CHECK IF KEY ONLY HAVE ALPHABET CHARACTERS
if (isalpha(key[i]) == 0) {
printf("Key must contain only alphabet characters\n");
return 1;
}
// CHECK IF KEY HAVE REPEATED CHARACTER
for (int i2 = 0; i2 < 26; i2++) {
if (i != i2) {
if (key[i] == key[i2]) {
printf("Key must have each character exactly one time\n");
return 1;
}
}
}
}
return 0;
}
string subs(string plain, string key) {
// GET PLAINTEXT LENGHT
int leng = strlen(plain);
// CREATES CIPHER STRING
string cipher = plain;
// CREATES AN ARRAY FOR UPPER KEY
int UPPER[26];
for (int i2 = 0; i2 < 26; i2++) {
if (isupper(key[i2]) > 0 ) {
UPPER[i2] = key[i2];
}
else {
UPPER[i2] = key[i2] - 32;
}
}
// CREATES AN ARRAY FOR LOWER KEY
int LOWER[26];
for (int i3 = 0; i3 < 26; i3++) {
if (islower(key[i3] > 0)) {
LOWER[i3] = key[i3];
}
else {
LOWER[i3] = key[i3] + 32;
}
}
for (int i = 0; i < leng; i++) {
if (isupper(plain[i]) > 0) {
cipher[i] = UPPER[plain[i] - 65];
}
else if (islower(plain[i]) > 0) {
cipher[i] = LOWER[plain[i] - 97];
}
else {
cipher[i] = plain[i];
}
}
return cipher;
}
It all leads me to think that it is a check50 problem, but with my lack of experience with coding and problem solving it can be anything.
Thanks in advance.
The line if (islower(key[i3] > 0)) { has parentheses in the wrong place. It should be:
if( islower(key[i3]) > 0 ){
or (more typical in C):
if( islower(key[i3]) ){
To get upper and lower working.
Lowercase needs to point to i2.
for (int i2 = 0; i2 < 26; i2++) {
if (islower(key[i2] > 0)) {
LOWER[i2] = key[i2];
}
else {
LOWER[i2] = key[i2] + 32;

Encryption does not output an ASCII string

In CS50, I'm trying to end my substitution exercise and I've an issue but don t know how to solve.
This is my code:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
int get_validkey(string Text);
int get_Alpha_to_code(char charac);
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("usage: ./substitution key\n");
return 1;
}
int validation = get_validkey(argv[1]);
if (validation != 0)
{
if(validation == 1)
{
printf("key must contain 26 alphabetical characters\n");
}
else
{
if (validation == 2)
{
printf("some charaters are not alphabetic\n");
}
else
{
printf("some charaters are repeated\n");
}
}
return 1;
}
else
{
// constants
string code = argv[1];
int charc;
int j;
// ask for message to encrypt
string tocode = get_string("plaintext: ");
// transform to code
int charcount = strlen(tocode);
char codedmessage[charcount];
for (int i = 0; i < strlen(tocode); i++)
{
// check type of character
if ((tocode[i]>='a' && tocode[i]<='z') || (tocode[i]>='A' && tocode[i]<='Z'))
{
j = get_Alpha_to_code(tocode[i]);
if(islower(tocode[i]))
{
codedmessage[i] = tolower(code[j]);
}
else
{
codedmessage[i] = toupper(code[j]);
}
}
else
{
codedmessage[i] = tocode[i];
}
}
codedmessage[strlen(codedmessage)] = '\0';
printf("ciphertext: %s", codedmessage);
printf("\n");
return 0;
}
}
// function assesses if the key input is valid and returns 0 if it is and 1 if it is not
int get_validkey(string Text)
{
int inputlength = strlen(Text);
if (inputlength != 26)
{
return 1;
}
else
{
for (int g = 0; g < 26; g++)
{
// checks if the character is non alphabetical
char chartest = toupper(Text[g]);
if (chartest < 'A' || chartest > 'Z')
{
return 2;
}
// scans all characters before A[g] to see if it has already been used
for (int k = 0; k < g; k++)
{
char beforechar = toupper(Text[k]);
if (chartest == beforechar)
{
return 3;
}
}
}
return 0;
}
}
int get_Alpha_to_code(char charac)
{
// define order for alphabet
const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char Alphachar = '\0';
// look at char position in alphabet
char chartest = toupper(charac);
// find position of charac in chain
int k = 0;
while (chartest != Alphachar)
{
Alphachar = Alphabet[k];
k++;
}
// send back char in code
return k - 1;
}
Check results are:
:) substitution.c exists :) substitution.c compiles :( encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key output not valid ASCII text
:( encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
output not valid ASCII text
:( encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
output not valid ASCII text
:( encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
output not valid ASCII text
:) 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
My results seems working because for 'A' I've 'Z', for 'a' I've got 'z', ...
But check system does not recognize my output as ASCII.
your problem is in that line :
codedmessage[strlen(codedmessage)] = '\0';
it can be
codedmessage[i] = '\0';
or
codedmessage[charcount] = '\0';
or more expensive for nothing
codedmessage[strlen(tocode)] = '\0';
because you cannot use strlen on codedmessage before to put the null character ending it (which is what you are trying to do), so you (may be) rewrite 0 exactly at the position you (may be) found it, that can be out of codedmessage and the behavior is undefined. In the next line printf writes the chars until it (may be) found a null char, so writing non initialized characters from it, the behavior is again undefined
Out of that, in :
int charcount = strlen(tocode);
char codedmessage[charcount];
for (int i = 0; i < strlen(tocode); i++)
you know the length is charcount why do you callstrlen(tocode) each turn knowing it is unchanged and what is its value ?

My Cs50 Caesar cipher program works but it won't pass the check50 tests

My program works when I test it myself and, the expected outputs match the actual outputs in the check50 test. Yet, I still fail a majority of the tests.
Here is my code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
string get_plain(void);
int main(int argc, string argv[])
{
//checks to see if only one argument is inputted.
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
//Checks to see if the argument is an integer.
int key = atoi(argv[1]);
if (key == 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
//Grabs plaintext input off user
string plaintext = get_plain();
int j = strlen(plaintext);
//creates an array the size of the user string input.
char ciphar[j];
printf("ciphertext: ");
for (int i = 0; i <= j; i++)
{
//Checks to see if the input is uppercase.
if (plaintext[i] >= 'A' && plaintext[i] <= 'Z')
{
//Checks if the input and the key added do not exceed ascii limits on uppercase letters.
if (plaintext[i] + (key % 26) > 90)
{
ciphar[i] = plaintext[i] + (key % 26) - 26;
printf("%c", ciphar[i]);
}
else
{
ciphar[i] = plaintext[i] + (key % 26);
printf("%c", ciphar[i]);
}
}
//Checks to see if the input is uppercase.
else if (plaintext[i] >= 'a' && plaintext[i] <= 'z')
{
//Checks if the input and the key added do not exceed ascii limits on lowercase letters.
if (plaintext[i] + (key % 26) > 122)
{
ciphar[i] = plaintext[i] + (key % 26) - 26;
printf("%c", ciphar[i]);
}
else
{
ciphar[i] = plaintext[i] + (key % 26);
printf("%c", ciphar[i]);
}
}
else
{
printf("%c", plaintext[i]);
}
}
printf("\n");
return 0;
}
//Grabs plaintext input off user
string get_plain(void)
{
string plaintext = get_string("Plaintext: ");
return plaintext;
}
Here is the output I recieve from Check50
Results for cs50/problems/2020/x/caesar generated by check50 v3.0.10
:) caesar.c exists.
:) caesar.c compiles.
:( encrypts "a" as "b" using 1 as key
expected "ciphertext: b\...", not "ciphertext: b\..."
:( encrypts "barfoo" as "yxocll" using 23 as key
expected "ciphertext: yx...", not "ciphertext: yx..."
:( encrypts "BARFOO" as "EDUIRR" using 3 as key
expected "ciphertext: ED...", not "ciphertext: ED..."
:( encrypts "BaRFoo" as "FeVJss" using 4 as key
expected "ciphertext: Fe...", not "ciphertext: Fe..."
:( encrypts "barfoo" as "onesbb" using 65 as key
expected "ciphertext: on...", not "ciphertext: on..."
:( encrypts "world, say hello!" as "iadxp, emk tqxxa!" using 12 as key
expected "ciphertext: ia...", not "ciphertext: ia..."
:) handles lack of key
:( handles non-numeric key
timed out while waiting for program to exit
:) handles too many arguments
As you can see, the expected output and the actual output are the same. Yet, the test still fails.
If any could give me an idea what to do I'd be very grateful. I'm still learning so any critique on my code relevant and irrelevant to my question would be greatly appreciated as well.
The expected output and the actual output only look the same to humans. They are different, and the difference is not detectable by the human eye.
It is printing the terminating null byte from plain text, which is unprintable so you can't see it. The problem lies here for (int i = 0; i <= j; i++).

PSET 2: Vigenere Cipher partially working?

I have created the following code as an answer to CS50x PSET2: Vigenere and it works to some extent however when running through check50 I get some errors listed below:
:) vigenere.c exists.
:) vigenere.c compiles.
:) encrypts "a" as "a" using "a" as keyword
:( encrypts "barfoo" as "caqgon" using "baz" as keyword - output not valid ASCII text
:( encrypts "BaRFoo" as "CaQGon" using "BaZ" as keyword - output not valid ASCII text
:) encrypts "BARFOO" as "CAQGON" using "BAZ" as keyword
:( encrypts "world!$?" as "xoqmd!$?" using "baz" as keyword- output not valid ASCII text
:( encrypts "hello, world!" as "iekmo, vprke!" using "baz" as keyword- output not valid ASCII text
:) handles lack of argv[1]
:) handles argc > 2
:( rejects "Hax0r2" as keyword - timed out while waiting for program to exit
What seems to be happening is where the key contains a high value (i.e z/Z) it causes the code to skip to the next line and miss out what appears to be random sequences. eg. in the first word of the string it missed out the 3rd character, then the second word it misses the 3rd and 4th and then the third word the 1st. I just can't understand what is happening.
I have used printf to ensure that all the variables being set and passed into functions are correct at runtime. The functions themselves are returning the correct responses (except validation of Hax0r2). I have tried debugging by comparing results to an online vigenere cipher tool.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int Validate1(int argc);
int Validate2(string argv);
void Cypher(string x);
void KeyCalc(string argv);
string MESSAGE;
int LENGTH;
int *KEY;
int COUNTER = 0;
int main(int argc, string argv[])
{
//Check if right amount of arguments are supplied
int Val1 = Validate1(argc);
if (Val1 == 0)
{
//Check if argument is a string of chars
int Val2 = Validate2(argv[1]);
if (Val2 == 0)
{
//get the string length
LENGTH = strlen(argv[1]);
//Dynamically update KEY array length
KEY = (int *)malloc(LENGTH * sizeof(*KEY));
if (KEY == NULL)
{
fprintf(stderr, "malloc failed\n");
}
//calculate the key
KeyCalc(argv[1]);
//get the message from the user to be encrypted
MESSAGE = get_string("plaintext: ");
printf("ciphertext: ");
//encrypt message from user
Cypher(argv[1]);
free(KEY);
return 0;
}
else
{
//validation failed
printf("Usage: ./vigenere keyword\n");
return 1;
}
}
else
{
//validation failed
printf("Usage: ./vigenere keyword\n");
return 1;
}
}
//Validate the number of arguments supplied
int Validate1(int argc)
{
if (argc != 2)
{
return 1;
}
else
{
return 0;
}
}
//Validate the argument is a string
int Validate2(string argv)
{
int k = 0;
//loop through all characters in argument line string and check if alphabetic
for (int i = 0; i < LENGTH; i++)
{
if isalpha(argv[i])
{
//Do Nothing
}
else
{
k++;
}
}
//k counts the number of non-alphabetic characters, so if > 0 then invalid input
if (k > 0)
{
return 1;
}
else
{
return 0;
}
}
void Cypher(string x)
{
//identify the length of the message to be coded
int Mlength = strlen(MESSAGE);
//identify the length of the key
int Slen = strlen(x);
//cycle through all characters in message supplied by user
for (int i = 0; i < Mlength; i++)
{
// loop through key
if (COUNTER > Slen - 1)
{
COUNTER = 0;
}
//check if the character is alphabetic
if (isalpha(MESSAGE[i]))
{
//convert the character to ASCII int value
char l = MESSAGE[i];
//add key value to message value and wrap around ascii mapping
if (isupper(MESSAGE[i]))
{
l = l + KEY[COUNTER];
if (l > 'Z')
{
l = l - 26;
}
}
else
{
l = l + KEY[COUNTER];
if (l > 'z')
{
l = l - 26;
}
}
//convert value back into character and store in array
MESSAGE[i] = (char) l;
// print character
printf("%c", MESSAGE[i]);
COUNTER++;
}
else
{
//character is 'numeric' or 'symbol' or 'space' just display it
printf("%c", MESSAGE[i]);
}
}
printf("\n");
}
void KeyCalc(string argv)
{
//convert key entry to values A/a = 0 to Z/z = 26
for (int i = 0; i < LENGTH; i++)
{
char k = argv[i];
if (islower(argv[i]))
{
KEY[i] = k - 'a';
}
else
{
KEY[i] = k - 'A';
}
}
}
encrypts "barfoo" as "caqgon" using "baz" as keyword
encrypts "BaRFoo" as "CaQGon" using "BaZ" as keyword
encrypts "world!$?" as "xoqmd!$?" using "baz" as keyword
encrypts "hello, world!" as "iekmo, vprke!" using "baz" as keyword
rejects "Hax0r2" as keyword
From the spec for the caesar pset:
...Caesar’s algorithm (i.e., cipher) encrypts messages by
“rotating” each letter by k positions. More formally, if p is some
plaintext (i.e., an unencrypted message), pi is the ith character in
p, and k is a secret key (i.e., a non-negative integer), then each
letter, ci, in the ciphertext, c, is computed as
ci = (pi + k) % 26
This algorithm (in either "case") does not do that:
l = l + KEY[COUNTER];
if (l > 'Z')
{
l = l - 26;
}
This walkthrough starting at 9:30 is a good primer on how to implement the "shift".
The proximate cause of the problem in this code is that this l = l + KEY[COUNTER]; can yield a result outside the ascii range. In the CS50 implementation, char defaults to a signed char. So, for example, 'r' + 'z' (as in "barfoo" ciphered with "baz") will yield -117.

Resources