I'm doing the CS50 Caesar problem and for the most part, my code works. I am not able to pass one of the check50 tests - my code does not handle non-numeric keys, and it timed out while waiting for the program to exit.
I have tried utilizing isdigit but it does not seem to work.
The check50 tests results copied pasted below:
:) caesar.c exists.
:) caesar.c compiles.
:) encrypts "a" as "b" using 1 as key
:) encrypts "barfoo" as "yxocll" using 23 as key
:) encrypts "BARFOO" as "EDUIRR" using 3 as key
:) encrypts "BaRFoo" as "FeVJss" using 4 as key
:) encrypts "barfoo" as "onesbb" using 65 as key
:) encrypts "world, say hello!" as "iadxp, emk tqxxa!" using 12 as key
:) handles lack of key
:( handles non-numeric key
timed out while waiting for program to exit
:) handles too many arguments
#include <stdio.h>
#include <cs50.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main (int argc, string argv[])
{
if (argc == 2 && (isdigit(argv[1] !=0))
{
int k = atoi (argv[1]); // convert string to int
string s = get_string ("plaintext: "); // obtain text
printf("ciphertext: ");
for (int i = 0; i < strlen(s); i++) // text loop
{
if (s[i] >= 'a' && s[i] <= 'z')
{
printf("%c", 'a' + ((s[i] - 'a') + k) % 26);
}
else if (s[i] >= 'A' && s[i] <= 'Z')
{
printf("%c", 'A' + ((s[i] - 'A') + k) % 26);
}
else
{
printf("%c", s[i]);
}
}
printf("\n");
return 0;
}
else
{
printf("./caesar key\n");
}
return 1;
}
I guess that the timing out is happening because your program is waiting for plaintext while the judge is not giving that because it excepts your program to exit right after giving non-numeric key.
You can use strtol(), which accepts a pointer to pointer to character and saves position of first invalid character.
Then, you can check if the input is numeric by checking if the returned pointer is pointing at the terminating null charcter.
char* p;
int k = (int)strtol (argv[1], &p, 10);
if (*p != '\0') {
puts("non-numeric key");
return 1;
}
Just loop through every digit of argv[1] and check if it's an integer.
for (int i = 0; i < strlen(argv[1]); i++)
{
if (isdigit(argv[1][i]) == 0)
{
return 1;
}
}
Related
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;
When I try to submit my work, the system told me to use the exit code. When I use return 0 and recheck, the system told me to use return 1...
(https://i.stack.imgur.com/dnVLV.png)
:) 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: is..."
:( handle lack of argv[1]
expected exit code 1, not 0
:( handles non-numeric key
timed out while waiting for program to exit
:( handles too many arguments
expected exit code 1, not 0
How can I fix it and what's wrong with my code?
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, string argv[])
{
int ok;
char r1;
if (argc == 2)
{
for (int i = 0, s = strlen(argv[1]); i < s; i++)
{
if (!isdigit(argv[1][i]))
{
printf("Sorry\n");
return 0;
}
else
{
ok = atoi(argv[1]);
string c = get_string("Enter:");
printf("ciphertext: ");
for (int t = 0, a = strlen(c); t < a; t++)
{
if (c[t] < 91 && c[t] > 64)
{
r1 = (c[t] - 64 + ok) % 26 + 64;
printf("%c", r1);
}
else if (c[t] < 123 && c[t] > 96)
{
r1 = (c[t] - 96 + ok) % 26 + 96;
printf("%c", r1);
}
else
{
printf("%c", c[t]);
}
}
return 0;
}
}
}
else
{
printf("Sorry\n");
}
printf("\n");
return 0;
}
I try to do well with my homework and all green...
There are multiple issues in your code:
you should return a non zero exit status upon error.
if the number given as a command line argument has more than 1 digit, you perform multiple iterations (one for each digit). You should move the encoding loop out of the first for loop.
using hard coded ASCII values for upper and lower case letters makes the code less portable and hard to read. You should use character constants 'A', 'Z', etc.
r1 = (c[t] - 64 + ok) % 26 + 64; is incorrect and may produce # instead of Z for some inputs. You should use r1 = (c[t] - 65 + ok) % 26 + 65; or better r1 = (c[t] - 'A' + ok) % 26 + 'A';
same mistake for r1 = (c[t] - 96 + ok) % 26 + 96;
Here is a modified version:
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "missing argument\n");
return 1;
}
char *arg = argv[1];
char *p;
int shift = (int)strtol(arg, &p, 10);
if (!(arg[0] >= '0' && arg[0] <= '9') || p == arg || *p != '\0') {
fprintf(stderr, "invalid shift argument: %s\n", arg);
return 1;
}
char *s = get_string("Enter string: ");
printf("ciphertext: ");
for (int t = 0; s[t] != '\0'; t++) {
unsigned char c = s[t];
/* assuming ASCII: upper and lowercase letters are contiguous */
if (c >= 'A' && c <= 'Z') {
c = (c - 'A' + shift) % 26 + 'A';
} else
if (c >= 'a' && c <= 'z') {
c = (c - 'a' + shift) % 26 + 'a';
}
putchar(c);
}
putchar('\n');
return 0;
}
You use the exit code by adding a return <value>, at the appropriate code line.
With <value> being what matches your interface definition, in case of your online judge it seems to be a 1.
In your code you at least fail to do so here:
else
{
printf("Sorry\n");
}
which should be
else
{
printf("Sorry\n");
return 1;
}
An alternative is the more explicit https://en.cppreference.com/w/c/program/exit for situations in which the path to the end of the program is not as obvious.
(This is mostly what the comment by Lundin mentions. I turned it into an explicit answer.)
However, to completely satisfy the judge you need to work on your output.
With the info given in the question, a solution for those problems is not possible.
Going through the CS50 exercise Caesar, and I'm still very new to C. My code to validate the key is all working, but as i try to convert from plaintext to the cipher text I keep getting caught up with the conversion form the ASCII number to the char. For example if I run the plaintext with the argv[1] as 27 I get /023/024. I recognize the math is off and if anyone has any pointers about that that would be great, but the main thing I'm curious about is why when I try to assign f into input[i] I get this "/000" type format.
string input = get_string("plaintext: ");
int len = strlen(input);
int i = 0;
while (input[i] != '\0')
{
int mod = atoi(argv[1]);
int t = input[i] + mod;
int f = t % 26;
input[i] = f;
i++;
}
printf("ciphertext: %s\n", input);
Your implementation of doing the %26 is wrong because when you do t % 26 you will get a letter but you have forgotten to add the ASCII value if it is a small of a larger one.
you must change your code to
Here is my code which I had used,
It is correct and hope it helps you...
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if (argc != 2) //checking if there are 2 arguments while calling the main method the first one here will be "./caesar" and the second will be the input given
{
printf("Wrong command\n");
return 1;
}
int k = atoi(argv[1]);//converting string input to integer
string a = argv[1];
for (int i = 0; i < strlen(a); i++)//checking if the input is a number for each character
{
if (a[i] < '0' || a[i] > '9')
{
printf("False\n");
return 1;
}
}
if (k < 0)
{
printf("Wrong command\n");
return 1;
}
else
{
//if everything is successful we ask for the plaintext
string code = get_string("plaintext: ");
//we just print 'ciphertext' and not the 'ciphered text'
printf("ciphertext: ");
for (int i = 0, n = strlen(code); i < n; i++)
{
if (islower(code[i]))
{
printf("%c", (((code[i] + k) - 97) % 26) + 97);
}
else if (isupper(code[i]))
{
printf("%c", (((code[i] + k) - 65) % 26) + 65);
}
else
{
printf("%c", code[i]);
}
}
printf("\n");
return 0;
}
}
Your mistake is that, you are not checking if it is an upper case or lower case and you are not subtracting 97 or 65 before doing the modulus...
If any other doubts, you are free to ask.
Here is the result after running check50 for the 2021 version
:) caesar.c exists.
:) caesar.c compiles.
:) encrypts "a" as "b" using 1 as key
:) encrypts "barfoo" as "yxocll" using 23 as key
:) encrypts "BARFOO" as "EDUIRR" using 3 as key
:) encrypts "BaRFoo" as "FeVJss" using 4 as key
:) encrypts "barfoo" as "onesbb" using 65 as key
:) encrypts "world, say hello!" as "iadxp, emk tqxxa!" using 12 as key
:) handles lack of argv[1]
All successful
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++).
Basically I have to create a caesar cipher, which is solely replacing each letter given with a letter that is int 'k' away. This takes 2 command line arguments: './caesar' and 'k', which given by the user. it works fine; but has one issue:
it encrypts "BARFOO" as "EDUIRR" using 3 as key which is correct
encrypts "BaRFoo" as "FeVJss" using 4 as key which is correct
BUT it does not encrypt "barfoo" as "onesbb" using 65 as key, it encrypts it as "oonneess|bb|bb" .
Please notice the punctuation; the caps and so on.
See the problem here? it also does this for other random words; it repeats letters. Help me....
PS: I am extremely new to programming, as you can see in my code, so please try to explain in english!
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
string s;
//int d;
int c;
int a;
if(argc != 2)
{
printf("Please run with a command line argument.");
return 1;
}
else
{
s = GetString();
}
int k = atoi(argv[1]);
for(int i = 0; i < strlen(s); i++)
{
a = s[i];
if(a<'A'||a>'z')
{
printf(" ");
}
else
{
if(a>='A'&&a<='Z')
{
c = a+k;
while(c>'Z')
{
c = 'A'+(c-'Z')-1;
printf("%c", c);
}
if(c<='Z')
{
printf("%c", c);
}
}
else if(a>-'a'&&a<='z')
{
c = a+k;
while(c>'z')
{
c = 'a'+(c-'z')-1;
printf("%c", c);
}
if(c<='z')
{
printf("%c", c);
}
}
}
}
printf("\n");
}
You can try to take k%26 since it should wrap around the characters of alphabet.
That should solve your problem.
You should try this way. Take the case of uppercase letters. First get the index from the letter 'A'.
index = a - 'A';
Then add the value in the variable k and get the remainder when divided with 26.
modified_index = ( index + k ) % 26;
Now to get the desired letter just add it with 'A'.
c = 'A' + modified_index;
Only adding k%26 will not help as that way 'z' with increment 1 will be turned into { which is wrong.
Also, if you just add value in k with the letter denoted by variable a it might cross the limit of ASCII characters as Joulin mentioned.