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

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;

Related

CS50x - PSET2- Substitution

I am having troubles with this submission, I don't see a problem with my code and it works fine when I test it but for some reason I don't know when I use check50, it returns this result:
:) substitution.c exists
:) substitution.c compiles
:( encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
Cause
output not valid ASCII text
:( encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
Cause
output not valid ASCII text
:( encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
Cause
expected "ciphertext: NJ...", not ""
:( encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
Cause
expected "ciphertext: Ke...", not ""
:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
Cause
expected "ciphertext: Cb...", not ""
:) encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
Cause
expected "ciphertext: Cb...", not ""
:( encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
Cause
expected "ciphertext: Rq...", not ""
:) handles lack of key
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key
This results mean that my code outputs the wrong result but when I test the keys and input text myself , it works as it is supposed to, I spent hours trying to figure it out but I am clueless here is my code :
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
bool contains(char c, char arr[], int n)
{
for(int i = 0 ;i < n; i++)
{
if(c == arr[i])
{
return true;
}
}
return false;
}
bool validkey(string key)
{
if (strlen(key) != 26 )
{
return false;
}
char l[26];
for (int i = 0; i < 26; i++)
{
if(isalpha(key[i]))
{
if(contains(key[i],l,i))
{
return false;
}
else
{
l[i] = key[i];
}
}
else
{
return false;
}
}
return true;
}
void cypher(string key, string s)
{
int n = strlen(s);
char r[n+1];
char t;
char a = 'a';
int pos = 0;
char w;
for(int i = 0; i < n; i++)
{
if(isalpha(s[i])){
t = tolower(s[i]);
pos = t - a;
if (islower(s[i]))
{
r[i]= tolower(key[pos]);
}
else if (isupper(s[i]))
{
r[i] = toupper(key[pos]);
}
}
else
{
r[i] = s[i];
}
}
r[n]='\0';
printf("ciphertext: %s\n",r);
}
int main(int argc, string argv[])
{
if(argc != 2)
{
return 1;
}
string key = argv[1];
if(!validkey(key))
{
printf("Invalid");
return 1;
}
string q = get_string("plain text: ");
cypher(key,q);
return 0;
}
Your actual problem looks to be the fact that you have overlooked the simple output required if no-input is provided, or if an invalid key is provided, e.g. from Pset2 - Substitution
And what if a user doesn’t provide a valid key?
$ ./substitution ABC
Key must contain 26 characters.
Or really doesn’t cooperate?
$ ./substitution
Usage: ./substitution key
You fail to provide the correct output in each circumstance.
Now your code is a bit rough as pointed out by #EugeneSh in the comments. Your contains() is a bit awkward and superfluous. What you need instead is a simple frequency-array. An array of 26-integers, initialized all zero, where you map the characters in key from 0-25 (by converting the char tolower() and subtracting 'a' so a-z maps to 0-25. Then for each character in key, you simply check if array[tolower(key[i]) - 'a'] then it is a duplicate char and return false. If not, increment that element and check the next char, e.g.:
#define KEYSZ 26
bool validkey (string key)
{
int keychk[KEYSZ] = {0};
if (strlen (key) != KEYSZ) {
return false;
}
for (int i = 0; key[i]; i++) {
int lowerk = tolower(key[i]) - 'a';
if (!isalpha (key[i]) || keychk[lowerk])
return false;
keychk[lowerk]++;
}
return true;
}
Where the keychk[] array is used for that purpose.
Your cypher() function should really return type string so the cipher text is available to the calling function, not simply output. (that isn't an error, just a practical consideration). You can rewrite and simplify your cypher() function (renamed: encipher()) as:
string encipher (string key, string s, string cipher)
{
int i = 0;
for (; s[i]; i++) {
if (isalpha (s[i])) {
int pos = tolower (s[i]) - 'a';
cipher[i] = islower(s[i]) ? tolower(key[pos]) : toupper(key[pos]);
}
else
cipher[i] = s[i];
}
cipher[i] = 0;
return cipher;
}
Those are really the only two functions needed. If you put it altogether you would have:
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define KEYSZ 26
bool validkey (string key)
{
int keychk[KEYSZ] = {0};
if (strlen (key) != KEYSZ) {
return false;
}
for (int i = 0; key[i]; i++) {
int lowerk = tolower(key[i]) - 'a';
if (!isalpha (key[i]) || keychk[lowerk])
return false;
keychk[lowerk]++;
}
return true;
}
string encipher (string key, string s, string cipher)
{
int i = 0;
for (; s[i]; i++) {
if (isalpha (s[i])) {
int pos = tolower (s[i]) - 'a';
cipher[i] = islower(s[i]) ? tolower(key[pos]) : toupper(key[pos]);
}
else
cipher[i] = s[i];
}
cipher[i] = 0;
return cipher;
}
int main (int argc, string argv[])
{
if (argc < 2) {
fputs ("Usage: ./substitution key\n", stderr);
return 1;
}
size_t len = 0;
string key = argv[1];
if (!validkey (key)) {
fputs ("Key must contain 26 characters.\n", stderr);
return 1;
}
string plain = get_string ("plaintext: "), cipher;
len = strlen (plain);
cipher = malloc (len + 1);
printf ("ciphertext: %s\n", encipher (key, plain, cipher));
free (cipher);
return 0;
}
(note: main() updated to allocate separately for cipher allocating for the number of characters in plain (+1 for the nul-terminating character) and changed output prompts, e.g. "plaintext " and "ciphertext: " as per the problem, and added #include <stdlib.h> which is not included by default with the online ide.cs50.io CS50 IDE)
Give that a try and let me know if you have problems or need additional help.

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 ?

CS50 - pset2 - substitution

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.

Encryption with substitution cipher does not generate valid ASCII output

I cant figure out why it says "output not valid ASCII text"!
In order for you to understand the context of the problem i post what is described to do.
IT STARTS HERE!
In a substitution cipher, we “encrypt” (i.e., conceal in a reversible way) a message by replacing every letter with another letter. To do so, we use a key: in this case, a mapping of each of the letters of the alphabet to the letter it should correspond to when we encrypt it. To “decrypt” the message, the receiver of the message would need to know the key, so that they can reverse the process: translating the encrypt text (generally called ciphertext) back into the original message (generally called plaintext).
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.
A message like HELLO, then, would be encrypted as FOLLE, replacing each of the letters according to the mapping determined by the key.
Let’s write a program called substitution that enables you to encrypt messages using a substitution cipher. At the time the user executes the program, they should decide, by providing a command-line argument, on what the key should be in the secret message they’ll provide at runtime.
Here are a few examples of how the program might work. For example, if the user inputs a key of YTNSHKVEFXRBAUQZCLWDMIPGJO and a plaintext of HELLO:
$ ./substitution YTNSHKVEFXRBAUQZCLWDMIPGJO
plaintext: HELLO
ciphertext: EHBBQ
Here’s how the program might work if the user provides a key of VCHPRZGJNTLSKFBDQWAXEUYMOI and a plaintext of hello, world:
$ ./substitution VCHPRZGJNTLSKFBDQWAXEUYMOI
plaintext: hello, world
ciphertext: jrssb, ybwsp
Notice that neither the comma nor the space were substituted by the cipher. Only substitute alphabetical characters! Notice, too, that the case of the original message has been preserved. Lowercase letters remain lowercase, and uppercase letters remain uppercase.
Whether the characters in the key itself are uppercase or lowercase doesn’t matter. A key of VCHPRZGJNTLSKFBDQWAXEUYMOI is functionally identical to a key of vchprzgjntlskfbdqwaxeuymoi (as is, for that matter, VcHpRzGjNtLsKfBdQwAxEuYmOi).
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Usage: ./substitution KEY");
return 1;
}
else if (argc == 2)
{
string text = argv[1];
string storing = text;
int counter = 0;
int i = 0;
bool number = true;
bool flag = true;
while (flag == true && i < 26)
{
if ((int)text[i] >= 48 && (int)text[i] <= 57)
{
number = false;
}
if (((int)text[i] >= 65 && (int)text[i] <= 90) || ((int)text[i] >= 97 && (int)text[i] <= 122))
{
counter++;
for ( int j = 0; j < counter - 1; j++)
{
if ((int)storing[j] == (int)text[i] || (int)storing[j] + 32 == (int)text[i])
{
flag = false;
}
}
}
i++;
}
if (number == false)
{
printf("Key must only contain alphabetic characters.");
return 1;
}
if (flag == false)
{
printf("Key must not contain repeated characters.");
return 1;
}
if (counter < 26)
{
printf("Key must contain 26 characters.");
return 1;
}
}
string plaintext = get_string("plaintext:");
string key = argv[1];
int counter;
bool not_letter;
bool capital1;
bool capital;
int crypto[strlen(plaintext)];
for (int i = 0; i < strlen(plaintext); i++)
{
capital1 = false;
capital = false;
not_letter = false;
if ((int)plaintext[i] >=65 && (int)plaintext[i] <= 90)
{
counter = (int)plaintext[i] - 65;
capital = true;
}
else if ((int)plaintext[i] >=97 && (int)plaintext[i] <= 122)
{
counter = (int)plaintext[i] - 97;
capital1 = true;
}
else
{
not_letter = true;
}
if (not_letter == true)
{
crypto[i] = (int)plaintext[i];
}
else if (capital == true)
{
if ((int)key[i] >=65 && (int)key[i] <= 90)
{
crypto[i] = (int)key[counter];
}
else if ((int)key[i] >=97 && (int)key[i] <= 122)
{
crypto[i] = (int)key[counter] - 32;
}
}
else if (capital1 == true)
{
if ((int)key[i] >=65 && (int)key[i] <= 90)
{
crypto[i] = (int)key[counter] + 32;
}
else if ((int)key[i] >=97 && (int)key[i] <= 122)
{
crypto[i] = (int)key[counter];
}
}
}
printf("ciphertext: ");
for (int i = 0; i < strlen(plaintext); i++)
{
printf("%c", (char)crypto[i]);
}
printf("\n");
return 0;
}
When i test the programm with check50 cs50/problems/2020/x/substitution of CS50 it says:
:) 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
output not valid ASCII text
! :( encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
output not valid ASCII text
:) handles lack of key
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key
Let's assume you've converted your key to uppercase.
Then let's simplify the heck out of your loop:
int keyIndex;
bool lowercase;
for (int i = 0; i < strlen(plaintext); i++) {
if (plaintext[i] >= 'A' && plaintext[i] <= 'Z') {
keyIndex = plaintext[i] - 'A';
lowercase = false;
} else if (plaintext[i] >= 'a' && plaintext[i] <= 'z') {
keyIndex = plaintext[i] - 'a';
lowercase = true;
} else {
// do not encrypt that character
ciphertext[i] = plaintext[i];
// and skip the rest of the loop
continue;
}
ciphertext[i] = key[keyIndex];
// revert back to lowercase, if necessary
if (lowercase) {
ciphertext[i] += 'a' - 'A';
}
}
The problem you've run into is that you didn't split up your big problem into small problems. So if you e.g. create an all uppercase key in advance, then you don't need to take it into account later on.
It's also very important to keep symmetry in your application. So if you assign a value in one part of the if then also do it in the else. And if you're able to stop early (such as when a character needs to be kept), then please do so.
It would of course be equally valid to convert the key to all lowercase and then convert characters back to uppercase where necessary.
Never ever use variable names such as counter or uppercase1. It makes your code very hard to read; variable names should be as clear as possible.
The ciphertext array is just a string / character array in the code above. If you're using C rather than C++ then you'd normally use a char* or char[], I presume.
Of course, this is still without using any separation between functions. It also doesn't use any platform functionality. If you manage to do that then debugging / maintaining your application will become even easier.

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