CS50 Caesar. invisible characters printing after upper case letters - c

I've written the below code for the Caesar project in CS50 and I felt like it was hitting the brief, but when I have run the check, both of the checks containing uppercase letters are coming back incorrect There seems to be some extra characters printed between each of the uppercase letters that don't appear when I run the code myself.
Am I missing something in my code that's causing this to happen?
the message from Check50
:( encrypts "BARFOO" as "EDUIRR" using 3 as key
expected "ciphertext: ED...", not "ciphertext: E\..."
:( encrypts "BaRFoo" as "FeVJss" using 4 as key
expected "ciphertext: Fe...", not "ciphertext: F\..."
my code
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
bool only_digits(string s);
char rotate(int, int);
int main(int argc, string argv[])
{
//make sure key is entered correctly
{
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
else
{
int cont = only_digits(argv[1]);
if (cont != 1)
{
return 1;
}
else
{
// convert key from string to int
int key = atoi(argv[1]);
string plain = get_string("plain text: \n");
// rotate the letters using the key
printf("ciphertext: ");
{
for (int i = 0, len = strlen(plain); i < len; i++)
{
rotate(plain[i], key);
}
}
printf("\n");
}
}
}
}
//rotate the letters using the key
char rotate(int p, int i)
{
// rotate uppers
if isupper(p)
{
p = p - 65;
char c = (p + i) % 26;
c += 65;
printf("%c", c);
}
// rotate lowers
if islower(p)
{
p = p - 97;
char c = (p + i) % 26;
c += 97;
printf("%c", c);
}
// if its a character keep the same
else
{
printf("%c", p);
}
return 0;
}
//make sure key entered is only digits
bool only_digits(string s)
{
for (int i = 0, len = strlen(s); i < len; i++)
{
if (!isdigit(s[i]))
{
printf("Usage: ./caesar key\n");
return 0;
}
else
{
;
}
}
return 1;
}

While I am unsure if it's your only bug, you forgot an else if in your rotate function such that upper case letters would print in the first case but also the else to the second case after they had been modified.
char rotate(int p, int i)
{
// rotate uppers
if isupper(p)
{
p = p - 65;
char c = (p + i) % 26;
c += 65;
printf("%c", c);
}
// rotate lowers
else if islower(p) // without else if here, upper cases would fall into the else below with p - 65
{
p = p - 97;
char c = (p + i) % 26;
c += 97;
printf("%c", c);
}
// if its a character keep the same
else
{
printf("%c", p);
}
return 0;
}

Related

Check50 "not a valid ASCII Ouput" on pset2 caesar

I completed the caesar assignment on cs50 and tested it on my terminal and it worked perfectly, but on check50 is kept failing some tests.
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
int getkey(string k);
string cipher(string s, int key);
int key;
int p;
int q;
int main(int argc, string argv[])
{
// Allow 2 command line inputs
if (argc == 2)
{
// Assign a local string to allow char scan
string s = argv[1];
// Check if all inputs are numbers
for (int i = 0; s[i] != 0; i++)
{
if (s[i] < 48 || s[i] > 57)
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// Get key from string
int cipherkey = getkey(s);
// Get user text
string text = get_string("plaintext: ");
// Calculate ciphertext and print
string ciphertext = cipher(text, cipherkey);
printf("ciphertext: %s\n", ciphertext);
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// Change string to int. Turns out theres already a function for this called atoi()
int getkey(string k)
{
key = 0;
for(int i = 0, conv = 0, n = strlen(k); k[i] != 0; i++, n--)
{
// Calcute the placevalue
p = pow(10, n-1);
conv = k[i] - 48; // Convert to int
key = key + (conv * p); // Sum up
}
return key % 26;
}
// Cipher text
string cipher (string s, int key)
{
for(int i = 0; s[i] != 0; i++)
{
if(islower(s[i]))
{
s[i] = s[i] + key;
while(s[i] > 122)
{
s[i] = (s[i] - 123) + 97;
}
}
else if(isupper(s[i]))
{
s[i] = s[i] + key;
while(s[i] > 90)
{
s[i] = (s[i] - 91) + 65;
}
}
}
return s;
}
with error message
:) caesar.c compiles.
:) encrypts "a" as "b" using 1 as key
:( encrypts "barfoo" as "yxocll" using 23 as key
output not valid ASCII text
:) 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
output not valid ASCII text
:) handles lack of argv[1]
:) handles non-numeric key
:) handles too many arguments
I wrote the code without knowing the "atoi" function so i implemented a function called getkey() to return key. when i returned key normally, it failed.
:( encrypts "barfoo" as "onesbb" using 65 as key
Output not a valid ASCII text
Until i returned key % 26;
I dont know why check50 isnt working although the program works well on my terminal. Please help.
Updated code:
#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
string cipher(string s, int key);
int main(int argc, string argv[])
{
// Allow 2 command line inputs
if (argc == 2)
{
// Assign a local string to allow char scan
string s = argv[1];
// Check if all inputs are numbers
for (int i = 0; s[i] != 0; i++)
{
if (s[i] < 48 || s[i] > 57)
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// Get key from string
int cipherkey = atoi(s);
// Get user text
string text = get_string("plaintext: ");
// Calculate ciphertext and print
string ciphertext = cipher(text, cipherkey);
printf("ciphertext: %s\n", ciphertext);
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// Cipher text
string cipher (string s, int key)
{
for(int i = 0; s[i] != 0; i++)
{
if(islower(s[i]))
{
s[i] = (int) s[i] + key;
while(s[i] > 'z')
{
s[i] = (s[i] - 123) + 97;
}
}
else if(isupper(s[i]))
{
s[i] = (int) s[i] + key;
while(s[i] > 'Z')
{
s[i] = (s[i] - 91) + 65;
}
}
}
return s;
}
I've rewritten the function trying to avoid going to far making corrections and improvements. Now, the copy is actually unnecessary, but I hope it helps you understand that a "signed (8 bit) char" is not 'wide' enough to use in calculations that lead to overflow...
Please read the following and try to follow along.
string cipher( string s, int key ) {
for( int i = 0; s[i] != '\0'; i++ ) {
if( !isalpha( s[i] ) )
continue;
int copy = (int)s[i]; // unnecessary casting, but...
// transform ASCII value into 0-25 range
if( islower( s[i] ) )
copy = copy - 'a';
else // must be uppercase
copy = copy - 'A';
copy = (copy + key) % 26;
// transform enciphered MODULO back into ASCII char
if( islower( s[i] ) )
copy = copy + 'a';
else // must be uppercase
copy = copy + 'A';
s[i] = copy;
}
return s;
}
Quiet night, so I thought I'd rewrite the rewrite just to see what it looked like...
string cipher( string s, int key ) {
for( char *cp = s; *cp; cp++ )
if( isalpha( *cp ) ) {
char c = "Aa"[ !!islower(*cp) ];
*cp = (char)(((*cp - c + key) % 26) + c);
}
return s;
}

CS50 Caesar - Check50 won't validate my code

I know that this question have been asked before, but I still can't find the clue to the problem in my code.
My program works apparently fine, but I'm not able to pass the check50 test. From what I understand, the issue may be related to the fact that the null \0 is printed. But I don't know how to modify that. Could you please help me?
This is my code:
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
bool only_digits(string s);
char rotate(char c, int n);
int main(int argc, string argv[])
{
string h = argv[1];
if (argc != 2 || !only_digits(h) || h <= 0)
{printf("Usage: ./caesar key\n");
return 1;
}
else
{
int key = atoi(argv[1]);
string plaintext = get_string("plaintext: ");
int f = strlen(plaintext);
printf("ciphertext: ");
for(int q = 0; q < f; q++)
{
printf("%c", rotate(plaintext[q], key));
}
printf("\n");
}
return 0;
}
bool only_digits(string s )
{
for (int i = 0, n = strlen(s); i < n; i++)
{
char digit = s[i];
if (!isdigit(digit))
return false;
}
return true;
}
char rotate(char c, int n)
{
if(isupper(c) && (c != '\0'))
{
printf("%c", (((c - 65) + n) % 26) + 65);
}
else
if(islower(c) && (c != '\0'))
{
printf("%c", (((c - 97) + n) % 26) + 97);
}
else
printf("%c", c);
return 0;
}
This is a caption of check50's check:
check50
rotate always returns 0, so printf("%c", rotate(plaintext[q], key)); is causing the letters you output to be interspaced with NUL characters.
I would keep that printf, but change rotate to return the character instead of printing it.

C command line argument validation errors

Thank you everyone who helped me yesterday. I have one more issue with my code.
The purpose of the program is to take a command line argument and use it as a key to encrypt some plaintext entered by the user and to shift it by the number of letters given. The program should accept a single command-line argument, a non-negative integer. If any of the characters of the command-line argument is not a decimal digit, the program should print the message Usage: ./caesar key and return from main a value of 1.
When I test it, I receive the following error messages:
handles lack of key
failed to execute program due to segmentation fault
:( handles non-numeric key
timed out while waiting for program to exit
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
//Declare the encrypting function
void encrypt(string plaintext, int k, string ciphertext);
int main(int argc, string argv[])
{
string plaintext;
int n = strlen(plaintext);
char ciphertext[n+1]; //the lenght of the plaintext+ 1 extra char which i
int k = atoi(argv[1]); //Convert string into an integer, i.e parsing
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
else
{
for (int i = 0, m = strlen(argv[1]); i < m; i++)
{
if (isdigit(argv[1][i]))
{
plaintext = get_string("Plaintext:");
encrypt(plaintext, k, ciphertext); //calling the encryption function
printf("ciphertext: %s\n", ciphertext);
return 0;
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
}
}
void encrypt(string plaintext, int k, string ciphertext)
{
for (int i = 0, n = strlen(plaintext); i < n; i++)
{
if (isupper(plaintext[i]))
{
int pi = plaintext[i] - 65;
char ci = ((pi + k) % 26) + 65;
ciphertext[i] = ci;
}
else if (islower(plaintext[i]))
{
int pi1 = plaintext[i] - 97;
char ci1 = ((pi1 + k) % 26) + 97;
ciphertext[i] = ci1;
}
else
{
ciphertext[i] = plaintext[i];
}
}
}
I've updated your code as little as possible. Most of it was just re-arranging things.
int main(int argc, string argv [])
{
// Check for correct number of args
if (argc != 2)
{
puts("Please supply a key.");
puts("Usage: ./caesar key");
return 0;
}
// Check that argv[1] is all digits (i.e. positive integer)
for (int i = 0, n = strlen(argv[1]); i < n; i++) {
if (!isdigit(argv[1][i])) {
puts("Usage: ./caesar key");
return 1;
}
}
// Get user string
string plaintext = get_string("Plaintext:");
if (!plaintext) return 1;
// My compiler does not allow VLAs so I use malloc
char *ciphertext = malloc(strlen(plaintext) + 1);
int k = atoi(argv[1]); //Convert string into an integer, i.e parsing
encrypt(plaintext, k, ciphertext); //calling the encryption function
printf("ciphertext: %s\n", ciphertext);
// Free up allocated mem
free(ciphertext);
free(plaintext);
return 0;
}
void encrypt(string plaintext, int k, string ciphertext)
{
int n = strlen(plaintext);
for (int i = 0; i < n; i++)
{
if (isupper(plaintext[i]))
{
int pi = plaintext[i] - 65;
char ci = ((pi + k) % 26) + 65;
ciphertext[i] = ci;
}
else if (islower(plaintext[i]))
{
int pi1 = plaintext[i] - 97;
char ci1 = ((pi1 + k) % 26) + 97;
ciphertext[i] = ci1;
}
else
{
ciphertext[i] = plaintext[i];
}
}
// Don't forget to append nul-terminator
ciphertext[n] = '\0';
}

CS50 Substitution - Does not output ciphertext - logic error

The function of the program is to be run with a command-line argument, 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.
e.g.
./substitution JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext: HELLO
ciphertext: VKXXN*
After starting the program with a valid command-line argument, the program doesn't output anything. It should output the ciphertext which has been encrypted using the key. I can't seem to find where the logic error is.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
bool is_valid_key(string plaintext);
int main(int argc, string argv[])
{
//Error message if user inputs incorret comand-line argument
if (argc != 2)
{
printf("Usage: ./substitution key\n");
return 1;
}
if (!is_valid_key(argv[1]))
{
printf("Key must contain 26 characters.\n");
return 1;
}
//Prompts user for plaintext
string plaintext = get_string("Plaintext: ");
string difference = argv[1];
for (int i = 'A'; i < 'Z'; i++)
{
difference[i - 'A'] = toupper(difference[i - 'A']) - i;
}
printf("Ciphertext: ");
for (int i = 0, len = strlen(plaintext); i < len; i++)
{
if(isalpha(plaintext[i]))
{
plaintext[i] = plaintext[i] + difference[plaintext[i] - (isupper(plaintext[i]) ? 'A' : 'a')];
}
printf("%c", plaintext[i]);
}
printf("\n");
}
//Checks vailidity of the key
bool is_valid_key(string plaintext)
{
int len = strlen(plaintext);
if (len != 26)
{
return false;
}
int freq[26] = { 0 };
for (int i = 0; i < len; i++)
{
if (!isalpha(plaintext[i]))
{
return false;
}
int index = toupper(plaintext[i]) - 'A';
if (freq[index] > 0)
{
return false;
}
freq[index]++;
}
return true;
}
Line 25 should have been for (int i = 'A'; i <= 'Z'; i++).
Line 29 should have been printf("ciphertext: ");

Vigenere Cipher

I am trying to make Vigenere Cipher in C. https://www.youtube.com/watch?v=9zASwVoshiM this is info about Vigenere Cipher. My code works doesnt work for certain cases like encrypts "world, say hello!" as "xoqmd, rby gflkp!" using "baz" as keyword instead it encrypts it as xomd, szz fl. Another example is:
encrypts "BaRFoo" as "CaQGon" using "BaZ" as keyword but instead it encrypts it as CakGo. My code is given below please help me out:
#include<stdio.h>
#include<cs50.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
int main(int argc, string argv[]) {
//string plaintext;
string key;
if (argc != 2) {
printf("Please run the programme again this time using a command line argument!\n");
return 1;
}
key = argv[1];
int keys[strlen(key)];
for (int m = 0; m< strlen(key); m++) {
if (isalpha(key[m]) == false) {
printf("Re-Run The programme without any symbols.\n");
return 1;
}
}
for (int b = 0; b < strlen(key); b++) {
if (isupper(key[b]) == false) {
keys[b] = key[b] - 'a';
}
else {
keys[b] = key[b] - 'A';
}
}
//printf("Enter a string which should be encrypted: \n");
string plaintext = GetString();
int plength = strlen(plaintext);
int klength = strlen(key);
string ciphertext = key;
for (int u = 0; u<plength; u++) {
if (isalpha(plaintext[u]) == false) {
printf("%c", plaintext[u]);
continue;
}
int value = u % klength;
ciphertext[u] = (keys[value] + plaintext[u]);
if ((islower(plaintext[u])) && (ciphertext[u])>'z') {
ciphertext[u] = ciphertext[u] - 'z' + 'a' - 1;
}
if ((isupper(plaintext[u])) && (ciphertext[u])>'z') {
ciphertext[u] = ciphertext[u] - 'Z' + 'A' - 1;
}
printf("%c", ciphertext[u]);
}
printf("\n");
return 0;
}
string ciphertext = key;
ciphertext should start out as blank, it should not be set to key.
ciphertext[u] = ciphertext[u] - 'z' + 'a' - 1;
This is not correct because ciphertext[u] can go out of range. Character should be between 'a' to 'z'. Use the mod % operator to make sure character is within range. For example:
int j = 0;
for (int u = 0; u<plength; u++)
{
int c = plaintext[u];
if (isalpha(plaintext[u]))
{
int k = keys[j % klength];
if (islower(c))
{
c = 'a' + (c - 'a' + k) % 26;
}
else
{
c = 'A' + (c - 'A' + k) % 26;
}
j++;
}
ciphertext[u] = c;
}
printf("%s\n", ciphertext);
Lots of little issues (ciphertext needs to be allocated dynamically and start out as a copy of plaintext, not key; need to mod calculations to the length of the alphabet; incorrect calculations; print errors to stderr) and lots of little optimizations that can be made (combine loops; combine if clauses; save key length to a variable earlier). A rework of your code:
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[]) {
if (argc != 2) {
fprintf(stderr, "Please run the program again with a command line argument!\n");
return 1;
}
string key = argv[1];
int key_length = strlen(key);
int keys[key_length];
for (int i = 0; i < key_length; i++) {
if (!isalpha(key[i])) {
fprintf(stderr, "Re-run The program without any symbols.\n");
return 1;
}
keys[i] = toupper(key[i]) - 'A';
}
// printf("Enter a string which should be encrypted: \n");
string plaintext = GetString();
int text_length = strlen(plaintext);
string ciphertext = strcpy(malloc(text_length + 1), plaintext);
for (int i = 0, j = 0; i < text_length; i++) {
if (!isalpha(plaintext[i])) {
continue;
}
int index = j++ % key_length;
ciphertext[i] = (toupper(plaintext[i]) - 'A' + keys[index]) % (1 + 'Z' - 'A');
ciphertext[i] += isupper(plaintext[i]) ? 'A' : 'a';
}
printf("%s\n", ciphertext);
free(ciphertext);
return 0;
}
Encrypts "world, say hello!" as "xoqmd, rby gflkp!" with key "baz"
Encrypts "BaRFoo" as "CaQGon" with key "BaZ"

Resources