CS50 Substitution - Does not output ciphertext - logic error - c

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: ");

Related

CS50 Caesar. invisible characters printing after upper case letters

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;
}

substitution cs50 - handle repeating characters in key

This code is for the CS50 Harvard course Pset 2 substitution.
one section of my program requires a check on the key to make sure characters are not repeated. I am failing this check as it reads ' :( Handles duplicate characters in Key - timed out while waiting for program to exit'
What needs to be fixed in my code to pass this final check?
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, string argv[])
{
printf("\n");
//check if we have correct number of command line arguments
if (argc != 2)
{
printf("Usage: ./substitution key\n");
printf("(Please enter only 2 command line arguments)\n");
return 1; //error
}
//initialize global variables for use//
//length of key
int s = strlen(argv[1]);
//copy of key may be altered if it is not valid yet
string k = argv[1];
// if else to check if our key is 26 characters
if (s == 26)
{
for (int i = 0; i < 26; i++)
{
//checks each index in k to see if it is alpha or not, throws error if not
if (!isalpha(k[i]))
{
printf("Usage: ./substitution key\n");
printf("(Key must be alphabetical)\n");
return 1; //error
}
}
for (int i = 0; i < s; i++)
{
for (int j = i + 1; j < s; j++)
{
if (isupper(k[i]))
{
k[i] = tolower(k[i]);
}
if (k[i] == k[j])
{
printf("Usage: ./substitution key\n");
printf("(Key can not have repeating characters)\n");
return 1; //error
}
}
}
}
else
{
//if we dont have 26 characters
printf("Usage: ./subsitution key\n");
printf("Key must be 26 characters\n");
return 1;
}
//true key for our cipher
string key = k;
//ask user for plaintext
string plaintext = get_string("Plaintext: ");
int n = strlen(plaintext);
printf("ciphertext: ");
char *ciphertext = malloc(n);
for (int i = 0; i < n; i++)
{
//plaintext = Hello (H - 65 = 7 (8TH INDEX) IN OUR KEY)
if (isalpha(plaintext[i]))
{
if (isupper(plaintext[i]))
{
int j = plaintext[i] - 'A';
printf("%c", toupper(key[j]));
}
if (islower(plaintext[i]))
{
int q = plaintext[i] - 'a';
printf("%c", tolower(key[q]));
}
}
else if (isdigit(plaintext[i]))
{
printf("%c", plaintext[i]);
}
else
{
printf("%c", plaintext[i]);
}
}
printf("\n");
}
check50 tells you what argument it uses for the test. When you run your code with that argument, does it complain about duplicate characters in the key?
This program will not find a duplicate if the second occurance of a character is in upper case.

CS50 Substitution

This problem set requires us to code a programme that takes a key from the user from the CLI and then an input (plaintext) and return a ciphertext version that is scrambled based on the key provided.
My code returns the correct ciphertext given any key and plaintext, however, the apparent output when using the in-built check50 module from cs50 is "", an empty string.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
string encrypt(string keys, string inputs);
char newkey;
string ciphertext;
int main(int argc, string argv[])
{
if (argc == 1)
{
printf("Please enter a key!\n");
return 1;
}
if (argc > 2)
{
printf("You can only have one key. The key must not have any spaces!\n");
return 1;
}
if (0 < strlen(argv[1]) && strlen(argv[1]) < 26)
{
printf("Key must contain 26 characters!\n");
return 1;
}
for (int j = 0; j < strlen(argv[1]); j++)
{
if (!((argv[1][j] >= 'a' && argv[1][j] <= 'z') || (argv[1][j] >= 'A' && argv[1][j] <= 'Z')))
{
printf("Key must contain alphabets only!");
return 1;
}
}
for (int k = 0; k < strlen(argv[1]); k++)
{
for (int l = (k + 1); l < strlen(argv[1]); l++)
{
if (argv[k] == argv[l])
{
printf("There can be no duplicate alphabets in the key!");
return 1;
}
}
}
string key = argv[1];
string input = get_string("plaintext: ");
encrypt(key, input);
printf("ciphertext: %s\n", ciphertext);
}
string encrypt(string keys, string inputs)
{
char ciphertexts[strlen(inputs)];
for (int i = 0; i < strlen(inputs); i++)
{
if (islower(inputs[i]))
{
int index = inputs[i] - 97;
newkey = keys[index];
ciphertexts[i] = tolower(newkey);
}
else if (isupper(inputs[i]))
{
int index = inputs[i] -65;
newkey = keys[index];
ciphertexts[i] = toupper(newkey);
}
else
{
ciphertexts[i] = inputs[i];
}
}
ciphertext = ciphertexts;
printf("%s\n",ciphertexts);
printf("%s\n",ciphertext);
return ciphertext;
}
The errors are as follows:
:( encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
expected "ciphertext: Z\...", not ""
This means that using a key of ZYXWVUTSRQPONMLKJIHGFEDCBA, and a plaintext of "A", "Z" is expected, but my programme outputs "".
I have a printf statement printing the cipher text, but it is somehow not captured
Memory management
Rather than return a pointer to a local variable (which is no longer valid after function ends #kaylum), pass into encrypt() a pointer to where to form the encrypted string.
Don't forget about '\0'
In forming the encrypted string account for the null character. #kaylum
Avoid repeated calls to strlen(inputs)
Advanced: Avoid negative ch with is...(ch)
Along with some other ideas, perhaps:
#include <ctype.h>
void encrypt(char *ciphertexts, const char *keys, char *inputs) {
// Let's work with unsigned char to avoid trouble negative char
const unsigned char *ukeys = (const unsigned char*) keys;
const unsigned char *uinputs = (const unsigned char*) inputs;
unsigned char *uciphertexts = (unsigned char*) ciphertexts;
size_t i = 0;
// Code uses a do loop to catch the null character.
do {
if (islower(uinputs[i])) {
int index = uinputs[i] - 'a';
unsigned char newkey = ukeys[index];
uciphertexts[i] = tolower(newkey);
} else if (isupper(uinputs[i])) {
int index = uinputs[i] - 'A';
unsigned char newkey = ukeys[index];
uciphertexts[i] = toupper(newkey);
} else {
uciphertexts[i] = uinputs[i];
}
} while (uinputs[i++]);
}
Code still has trouble when the locale is not "C" or with non-ASCII, but we can leave those as advanced issues.
Advanced
Since strings to encrypt can be lengthy, consider pre-forming a key valid for all char. I.e.
unsigned char keys256[UCHAR_MAX + 1] = { 0 };
for (i = 0; i <= UCHAR_MAX; i++) {
keys256[i] = i;
}
for (i = 0; key[i]; i++) {
keys256['a' + i] = tolower(key[i]);
keys256['A' + i] = toupper(key[i]);
}
and then in encrypt(..., keys256, ...), use a greatly simplified loop.
...
do {
uciphertexts[i] = keys256(uinputs[i]);
} while (uinputs[i++]);
...

Why do 2 letters not equal to each other show up as equivalent in C?

I am making a program that does a simple encryption to a plaintext and outputs an encrypted ciphertext. Here is an example with YTNSHKVEFXRBAUQZCLWDMIPGJO being the key.
$ ./substitution YTNSHKVEFXRBAUQZCLWDMIPGJO
plaintext: HELLO
ciphertext: EHBBQ
You can read the directions here if you would like to know more.
Here is my code:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
string ciphertext_generator(string key,string plaintext);
bool alphabetic_char_checker(string word);
bool alphabetic_char_repeat_checker(string word);
string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Please enter a key\n ");
return 1;
}
if (strlen(argv[1]) != 26)
{
printf("The key must contain 26 characters.\n ");
return 1;
}
if (alphabetic_char_checker(argv[1]) == false)
{
printf("The key must only contain alphabetic characters.\n ");
return 1;
}
else if (alphabetic_char_repeat_checker(argv[1]))
{
printf("The key must not contain repeated characters.");
return 1;
}
string plaintext = get_string("plaintext: ");
string ciphertext = ciphertext_generator(argv[1],plaintext);
printf("ciphertext: %s",ciphertext);
return 0;
}
string ciphertext_generator(string key,string plaintext)
{
string ciphertext = plaintext;
for (int i = 0, k = strlen(ciphertext); i < k; i++)
{
bool capital = isupper(ciphertext[i]);
ciphertext[i] = toupper(ciphertext[i]);
for (int i2 = 0, a = strlen(alphabet); i2 < a; i2++)
{
if (plaintext[i] == alphabet[i2])
{
if (capital == true)
{
ciphertext[i] = key[i2];
}
else
{
ciphertext[i] = tolower(key[i2]);
}
}
}
}
return ciphertext;
}
bool alphabetic_char_checker(string word)
{
for (int i = 0, w = strlen(word); i < w; i++)
{
word[i] = toupper(word[i]);
if (word[i] < 'A' || word[i] > 'Z')
{
return false;
}
}
return true;
}
bool alphabetic_char_repeat_checker(string word)
{
for (int i = 0, w = strlen(word); i < w; i++)
{
word[i] = toupper(word[i]);
for (int i2 = 0; i2 < w; i2++)
{
if (word[i] == word[i2])
{
return false;
}
}
}
return true;
}
My question lies in the ciphertext_generator function that takes in a key and a plaintext and outputs an encrypted ciphertext. What happens here is that it iterates over ever letter in the plaintext and then for each letter, it iterates over the entire alphabet checking to see if the letter in the plaintext is in the alphabet and counts which index it is in the alphabet which then is then used as the index for the key which is then replaced correspondingly in the ciphertext string.
The problem here is that on the first letter of the plaintext is iterating over every letter of the alphabet and it correctly finds and replaces the correct letter at the correct index which is N but it replaces the plaintext too and not just the ciphertext.
I am not trying to solve this problem just trying to understand what is going on as I have already debugged this. What happens next is that the first letter keeps iterating through the rest of the alphabet and until it finds N and then replaces that with correct letter from the alphabet but with the plaintext too.
**
The Questions
Why does plaintext pick up letters when I only set the ciphertext to do so?
To answer the question
Why does plaintext pick up letters when I only set the ciphertext to do so?
On the first line of your function ciphertext_generator it says string ciphertext = plaintext; This lines states that the ciphertext variable will be equal to the plaintext variable meaning the memory of ciphertext points to the same memory as plaintext so any changes made to ciphertext will be applied to plaintext.

Pset 2 CS50 Substitution Problem, If statement assigns variables(i guess)

I created a code for pset2 problem, named substitution in C.
The programs takes an String of Characters in command-line and apply it to a given text and changes it, printing the ciphertext.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
//First part, checks KEY input
if (argc != 2) // checks quantity of command-line arguments
{
printf("Usage: ./substitution KEY\n");
return 1;
}
int i = 0;
int largo = strlen(argv[1]);
while (i < largo) //checks if char are alphabetic
{
if (isalpha(argv[1][i]) == 0)
{
printf("Key must contain only alphabetic characters\n");
return 1;
}
else //changes to uppercase
{
argv[1][i] = toupper(argv[1][i]);
}
i++;
}
if (i != 26) //checks length of Key
{
printf("Key must be 26 characters long\n");
return 1;
}
for (int j = 0; j < largo - 1; ++j) //checks repetition of characters
{
for (int k = j + 1; k < largo; ++k)
{
if (argv[1][j] == argv[1][k])
{
printf("Key must not have duplicated characters\n");
return 1;
}
}
}
//second part asks for input
//declaration of strings to get and transform
string plaintext = get_string("plaintext:");
string alfabeto = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string ciphertext = plaintext;
//third part: cifrado
for (int x = 0; x < strlen(plaintext); ++x)
{
if (isalpha(plaintext[x]) != 0)
{
int lower = 0;
if (islower(plaintext[x]) != 0) // is lower case?
{
lower = 1;
plaintext[x] = toupper(plaintext[x]);
}
for (int y = 0; y < strlen(alfabeto); ++y)
{
//printf("alfa: %c text: %c\n", alfabeto[y], plaintext[x]);
if (alfabeto[y] == plaintext[x])
{
ciphertext[x] = argv[1][y];
//printf("c: %s\n", ciphertext);
break;
}
}
if (lower == 1)
{
ciphertext[x] = tolower(ciphertext[x]);
}
}
}
printf("ciphertext: %s", ciphertext);
printf("\n");
return 0;
}
The issue is that the last if statement, kind of assigns the character in "ciphertext" to the "plaintext" variable, so i had to put a break statement in it, to stop that.
Any guesses why this program doesn't work as intended when there's no "break" statement?
if (alfabeto[y] == plaintext[x])
{
ciphertext[x] = argv[1][y];
//printf("c: %s\n", ciphertext);
break;
}

Resources