Getting Caesar Cipher to wrap around - c

I can get it to print the plaintext and and shift by the key value, but
i'm a bit confused on how to get the letters to wrap around, and how to implement it into my code.
Any suggestions would be appreciated.
Thank you.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
//Gets number of user arguments and the key.
int main (int argc, string argv[]) {
if(argc != 2) {
printf("try again\n");
}
//Converts string to int.
int key = atoi(argv[1]);
//Will store the chars + key.
int result;
printf("Please enter what you would like to encrypt: ");
//Gets plaintext from user.
string plainText = get_string();
//Iterates over the user's input, checking for the case of each char.
for (int i = 0; i <= strlen(plainText); i++) {
if (toupper(plainText[i]) || tolower(plainText[i])) {
result = plainText[i];
}
//Checks if i'th char is a letter and shifts it.
if (isalpha(plainText[i])) {
result = plainText[i + key];
}
}
printf("%c", result);
}

One of the neatest tricks to do this is to use the modulo % operator.
Now talking about your code,
for (int i = 0; i <= strlen(plainText); i++) {
if (toupper(plainText[i]) || tolower(plainText[i])) {
result = plainText[i];
}
//Checks if i'th char is a letter and shifts it.
if (isalpha(plainText[i])) {
result = plainText[i + key];
}
}
printf("%c", result);
This code makes no sense to me.
Your first if condition is I guess to distinguish of the not alphabetical characters, so the if condition could be something like if (! isalpha(plainText[i]) ,
Then your second condition is to add the key to the character if it is an alphabet. It should be something like
if (isalpha (plainText[i])) {
if (islower(plainText[i])
result = ((plainText[i] - 'a') + key) % 26 + 'a';
else
result = ((plainText[i] - 'A') + key) % 26 + 'A';
}
Explanation of above logic:: First you check weather the letter is lowercase or uppercase, so that you can make it in range of 0 to 26,
Then you add the key with the modulo of key, so that it can circle back to 0, then you again convert that it to ascii by adding the value of 'a' their.
e.g. if plainText[i] = 'x' (ascii value 120) and key = 5, then
plainText[i] = 120
plaintext[i] - 'a' = 23
(plaintext[i] - 'a') + key = 28 // Out of 0-25 alphabet range
((plaintext[i] - 'a') + key) % 26 = 2 // Looped back
(((plaintext[i] - 'a') + key) % 26) + 'a' = 99 (ascii value for 'c')
So as you can see we got c after adding 5 to x
And finally the position of your print should be inside loop, otherwise it's gonna only print the last input, which is not correct.
I hope I did everything to help you, keeping in mind the CS50's Honor Code. And also I would suggest you to ask these questions in their forums, because they are a more knoledgeble community to use <cs50.h>
Also, enjoy CS50, it is one of the best CS courses to get you started ;)

Related

Why does the output of the code changes, when printing a random text with printf?

The following code changes the letters of the input by k. k is the first argument. For example, if the input should be changed by 2 letters the command line argument would be "./caesar 2", if three then "./caesar 3". etc.
Changing letters means, for example, change by 2, then input 'a' becomes 'c'. Change by 3 means input "abc" becomes "def", etc.
The input provided by the user is being checked if (a) the number arguments is exact 2, (b) the argument is a number.
The code executed as the code is written below the output is shortened by one letter. For example, "hello" changed by 1 letter becomes "iffm".
If one letter is entered only, it shows the correct output follwed by some undefined letters. For example, 'a' becomes "b��P" or "bm>�" or "b;���".
When either (1) the input check (b) [if the argument is a number] is removed OR (2) a printf line with a random statement (it can even be an empty string) is inserted EXACTLY between the get_string function, when asking the user for input and the for-loop, when changing the letters the output is as intended. Or (3) if the input's last character is a special character, the output is as expected (special character is any non-alphabetical character). for example, "hello1" or "hello!" changed by one letter becomes "ifmmp1" or "ifmmp!".
I am really desperate and I don't know what is happening and more importantly why this is happening.
So my questions are:
(1) Why is output shortened by one letter? Why is the output wrong, when the input is one letter only? (I guess it's the same problem).
(2)
(a) Why does the output change when either the number check is removed or
(b) a random printf line is inserted exactly between the lines mentioned above or
(c) the last character is a non-alphabetical character?
I really appreciate any help and please excuse any weird English as it is not my native language :). Thanks a lot! A desperate code learner :)
This is the code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
bool isNumber(string numberToCheck);
int main(int argc, string argv[])
{
// checking, if arguments are correct
// checking, if input is correct (i.e. 2)
if(argc != 2)
{
printf("Usage: ./cesar key\n");
exit(1);
}
// checking, if input is a number, if the following if statement is removed the output changes
if(!isNumber(argv[1]))
{
printf("Usage: ./caesar key\n");
exit(1);
}
// variables
int k = atoi(argv[1]);
string plaintext;
int plaintextLength;
// getting the plaintext from user input, creating ciphertext string of same length
plaintext = get_string("paintext: ");
// checking the length of the input
plaintextLength = strlen(plaintext);
//the output changes, when the next line is being inserted
printf("");
// creating new empty string with plaintextLength
char ciphertext[plaintextLength];
// iterating through plaintext char by char
for(int i = 0; i < plaintextLength;i++)
{
// in case of capital letter
if(plaintext[i] >= 65 && plaintext[i] <= 90)
{
ciphertext[i] = 65 + (((plaintext[i] - 65) + k) % 26);
}
// else in case of small letter
else if(plaintext[i] >= 97 && plaintext[i] <= 122)
{
ciphertext[i] = 97 + (((plaintext[i] - 97) + k) % 26);
}
// else in case of non alphabetical letter
else
{
ciphertext[i] = plaintext[i];
}
}
printf("ciphertext: %s\n", ciphertext);
}
bool isNumber(string numberToCheck)
{
for(int i = 0;i < strlen(numberToCheck); i++)
{
if(!isdigit(numberToCheck[i]))
{
return false;
}
}
return true;
}
int i;
// iterating through plaintext char by char
for(i = 0; i < plaintextLength;i++)
{
// in case of capital letter
if(plaintext[i] >= 65 && plaintext[i] <= 90)
{
ciphertext[i] = 65 + (((plaintext[i] - 65) + k) % 26);
}
// else in case of small letter
else if(plaintext[i] >= 97 && plaintext[i] <= 122)
{
ciphertext[i] = 97 + (((plaintext[i] - 97) + k) % 26);
}
// else in case of non alphabetical letter
else
{
ciphertext[i] = plaintext[i];
}
}
ciphertext[i] = '\0';
printf("ciphertext: %s\n", ciphertext);
}
In C every string uses \0 to tell the computer that it has reached the end of a string.
Without this NULL terminating operator, the computer might try to read past the string into the memory, that is why you encounter random symbols.
So when you are creating a string make sure to add \0 at the end of it, this is what I did with your code here.
I declared "i" outside the for loop the it doesn't cease to exist at the end of the loop.
When you reach the end of the loop, "i" will be equal to the length of your string.
If my plaintext is "lol" and the key is 5, "i" will be equal to 3 and the ciphertext will be "qtq".
Ciphertext[i] will point just after the last "q" since we count from 0 an this is where you want to put your \0.
Also there is a typo in your getstring.
Hope my explaination is clear, if you have any question juste ask :)

CS50 Vigenere, code is almost done but I don't know what's missing?

#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
// two arguments
if (argc != 2)
{
printf("Give two arguments\n");
return 1;
}
printf("plaintext: ");
string plaintext = get_string();
printf("ciphertext: ");
string key = argv[1];
for (int i = 0, t = 0, n = strlen(plaintext); i < n; i++, t++)
{
// if it's no letter, then:
if (!isalpha(plaintext[i]) && plaintext[i] != ' ')
{
printf("False");
return 1;
}
int number = 0;
if (isalpha(plaintext[i]))
{
number += 1;
}
if (strlen(key) > number)
{
number = 0;
}
if (isupper(plaintext[i]))
{
printf("%c", (((plaintext[i] - 65) + key[number]) % 26) + 65);
}
//if it is lowercase
else if (islower(plaintext[i]))
{
printf("%c", (((plaintext[i] - 97) + key[number]) % 26) + 97);
}
else
{
printf("%c", plaintext[i]);
}
}
printf("\n");
}
So there's something missing with my code. When I do ./vigenere baz and then type as plaintext: Hello, world!, I get ciphertext: ByffiFalse. I should be getting iekmo, vprke! Also, when I type ./vigenere hello, and then type bye as the plaintext, I get ciphertext bye too while it should be icp. Can someone figure out what's missing or wrong with my code?
The biggest two problems with your code are the calculating the correct key differential value (you're not), and key advancement. I'll talk about them in reverse order.
Key advancement should start with the first key character, then advance one by one with each plain text being processed. When the key position reaches end-of-string, it is restarted. The most basic pseudo code for that would be
char *keyp = argv[1];
for (loop through plainttext)
{
if (*keyp == 0) // reached the terminator ?
keyp = argv[1]; // then reset to beginning.
//... process the current plain text character, using *keyp
//... as the next key character to use.
// advance key to next position (possibly conditionally)
++keyp;
}
But your code doesn't do that. Rather, it advances the key immediately, meaning you're starting with the second character onward.
int number = 0;
if (isalpha(plaintext[i]))
{
number += 1; // HERE. first pass will use key[1]. it should be key[0]
}
if (strlen(key) > number) // this is backward
{
number = 0;
}
Secondly, and probably more important, the whole point if a Vigenere cipher is effectively using a square shading table. See this link for a picture of that. The point of the algorithm you're coding is to act like that table exists using math. The offsets are the important part.When you do this calculation:
(((plaintext[i] - 65) + key[number]) % 26) + 65
which in reality should look like this:
(((plaintext[i] - 'A') + key[number]) % 26) + 'A'
consider what that key character addition is doing. Take your example:
key: baz
plaintext: Hello, World!
The first ciphertext character by your calculation will be:
((('H' - 'A') + 'a') % 26) + 'A'
Note: the 'a' is there because your first-pass is broken by one, remember?
That crunches down as follows
(((7) + 97) % 26) + 'A'
((105) % 26) + 'A'
(1 % 26) + 'A'
1 + 'A'
'B'
And that's exactly what you're getting. But its wrong. Its wrong because this is wrong:
(((plaintext[i] - 'A') + key[number]) % 26) + 'A'
^^^^^^^^^^^
That's the raw ascii value of the input character. What it should be is a calculated value between 1..26. In short, you're not adjusting your key input correctly.
Assumptive Solution
The following assumes the key will always be lower-case. It also fixes your first-skip logic, and decouples using cs50.h (which, frankly, I think does more harm than good). Finally it uses a `char* to track which key character is being used next. I leave the task of supporting mixed case input keys to you:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
// two arguments
if (argc != 2)
{
printf("Give two arguments\n");
return 1;
}
printf("plaintext: ");
char pt[256] = { 0 };
if (fgets(pt, sizeof pt, stdin))
{
// get the plaintext length
size_t ptlen = strlen(pt);
// remove trailing newline if present, and adjust ptlen
if (ptlen > 0 && pt[ptlen - 1] == '\n')
pt[--ptlen] = 0;
// the key we're using. intially at the start
char *key = argv[1];
for (size_t i = 0; i < ptlen; ++i)
{
// reset key if prior iteration landed on terminator
if (!*key)
key = argv[1];
if (isalpha((unsigned char)pt[i]))
{
if (isupper((unsigned char)pt[i]))
{
printf("%c", (((pt[i] - 'A') + (*key-'a')) % 26) + 'A');
++key;
}
//if it is lowercase
else if (islower((unsigned char)pt[i]))
{
printf("%c", (((pt[i] - 'a') + (*key-'a')) % 26) + 'a');
++key;
}
else
{
fputc(pt[i], stdout);
}
}
else
{
fputc(pt[i], stdout);
}
}
fputc('\n', stdout);
}
else
{
perror("Failed to read string");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Output from ./progname baz
plaintext: Hello, World!
Iekmo, Vprke!
All non-alpha characters (not spaces only) should be skipped without encoding. Do not print "False" and return on, for example ',' symbol in "Hello, world!" string. Also, you can encode string in-place. Thus, main loop may looks like
printf("plaintext: ");
string s = GetString();
if (s == NULL)
return 1;
for (int i = 0, len = strlen(s); i < len; ++i) {
if (isalpha(s[i])) {
/* encode s[i] in-place,
* all non-alpha characters left as is
*/
}
}
printf("ciphertext: %s\n", s);
Key characters should also be "shifted". For example, for uppercase letters
s[i] = ((s[i] - 'A') + (key[n] - 'A') % 26) + 'A';
if (++n >= keylen)
n = 0;
I suggest to normalize key before main loop, so that you will be able to use (key[n] - 'A') both for lower and upper characters from input string:
string key = argv[1];
strupper(k);
int keylen = strlen(key);
int n = 0;
Although I don't want provide full code because this is your courses, I think it would be better if you do it by yourself. But… some pieces:
strupper function:
void strupper(string s)
{
for (int i = 0, n = strlen(s); i < n; ++i)
s[i] = toupper(s[i]);
}
Compact main loop:
for (int i = 0, n = strlen(s); i < n; ++i) {
if (isalpha(s[i])) {
char ref = isupper(s[i]) ? 'A' : 'a';
int shift = k[j] - 'A';
s[i] = ref + (s[i] - ref + shift) % 26;
if (++j >= klen) j = 0;
}
}
p.s. You use the same key character for all input characters because of int number = 0; defined and zeroed inside for loop.

Not understanding why 116+14 = -126 (CS50)

I'm trying to figure out the Vigenere problemset of CS50 and I'm not getting what I'm expecting. I wrote out the case I used on paper and did the math by hand and when I run the program I'm getting something entirely different. It's addition and the program is working for values that are not wrapping around the alphabet. I'm using the argument 'bacon' and the string received by the user is going to be 'Meet'. The program is running fine for the first 3 letters 'Mee', but when it reaches 't' it doesn't work too well...
The program runs by finding the ASCII value of the argument which is converted into uppercase, in this case it's 'BACON', and then those values are used to increment the corresponding i'th value in the string 'Meet'. I had my code show how much it was going to increment the i'th value in the string, and the value of the i'th character in the string. I'm not entirely sure what I'm doing incorrectly. Any help or tips would be greatly appreciated !
P.S. this problem involves the last 'else if' conditional within the last for loop.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
// make sure command-line receives only one argument, excluding file
name
if( argc != 2)
{
printf("incorrect # of arguments, terminating program\n");
return 1;
}
// make sure that only aplhabets are entered for argument
for(int i = 0,n=strlen(argv[1]); i<n; i++)
{
if(toupper(argv[1][i]) < 65 || toupper(argv[1][i]) > 90 )
{
printf("non-alphabetical character found in argument, terminating program\n");
return 2;
}
// convert argument to all UPPERCASE, to make lower and upper affect plain-text similarily
}
for(int i = 0,n=strlen(argv[1]); i<n; i++)
{
argv[1][i] = toupper(argv[1][i]);
}
printf("plaintext: ");
string plain = get_string();
printf("ciphertext: ");
int m = strlen(argv[1]);
for(int i = 0,j=0,n=strlen(plain); i < n; i++,j++)
{
if(j == m)
{
j = 0;
}
int increment = argv[1][i] - 65; // key: A = 0, B = 1
printf("value of increment: %i\n",increment);
if(plain[i] >= 65 && plain[i] <= 90) // check for case of plain-text
{
plain[i] = (int) plain[i] + increment;
if((int) plain[i] > 90) // see if wrapping occurs
{
increment = (int) plain[i]%90;
plain[i] = 65 + increment;
}
}
else if(plain[i] >= 97 && plain[i] <= 122)
{
printf("letter %c, value %i\n",plain[i],(int)plain[i]);
plain[i] = (int)plain[i] + increment;
printf("position %i, value %i\n",i,(int)plain[i]);
if((int) plain[i] > 122)
{
increment = (int) plain[i]%122;
plain[i] = 97 + increment;
}
}
else{
j--;
}
}
printf("%s\n",plain);
}
Your program is using signed char variables, which have a range that goes from -128 to +127, and you are experiencing an integer overflow.

CS50 pset2 Vigenere code - Outputs one incorrect letter

New to this site and to programming. I've looked through the previous questions under this topic and tried any number of fixes, but I keep having the same problem.
My program runs fine and gives me the output I expect, with the exception of the letter 'B' or 'b'. Every other letter encrypts as it should. Where did I go wrong?
EDIT - When I encrypt the message, "Meet me at the park" with a key of "bacon", I should get: Negh zf av huf pcfx. Instead I get: Tegh zf av huf pcfx
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
//Get key in commandline argument. Prompt until key is given.
string key = argv[1];
int key_length = strlen(key);
if (argc != 2)
{
printf("Invalid command. Please specify key.");
return 1;
}
//Make sure only alphabetical chars are used in key.
for (int i = 0; i < key_length; i++)
{
if (!isalpha(key[i]))
{
printf("Invalid command. Please specify key.");
return 1;
}
}
//Get message to be encrypted
string plain = GetString();
for (int i = 0, j = 0; i < strlen(plain); i++)
{
if (isalpha(plain[i]))
{
if (isupper(plain[i]))
{
plain[i] = (((plain[i] - 65) + (key[j%key_length] - 65)) % 26) + 65;
j++;
}
else
{
if (islower(plain[i]))
{
plain[i] = (((plain[i] - 97) + (key[j%key_length] - 97)) % 26) + 97;
j++;
}
}
}
}
printf("%s\n", plain);
return 0;
}
You're assuming that the plaintext letters are the same case as the key letters. The problem you're seeing with the capital M not being encoded correctly is because of this.
First convert all characters in the key to upper case:
if (!isalpha(key[i]))
{
printf("Invalid command. Please specify key.");
return 1;
} else {
key[i]=toupper(key[i]);
}
Then when you're encoding, assume the key is uppercase. Also, instead of adding or subtracting the ASCII values for A and a, just use the character constants instead. It makes your code more readable.
You also don't need separate isupper and islower checks since you're already calling isalpha at the prior level up.
if (isupper(plain[i]))
{
plain[i] = (((plain[i] - 'A') + (key[j%key_length] - 'A')) % 26) + 'A';
j++;
}
else
{
plain[i] = (((plain[i] - 'a') + (key[j%key_length] - 'A')) % 26) + 'a';
j++;
}
There are four cases to consider
plain[i] key[j]
------------------
lower lower
lower upper
upper lower
upper upper
Your code only handles two of those cases.
Side note: 65 should be written as 'A' and similarly for all of the other hard-coded numbers.

Array comparison and Loops In C

I am taking this free online course so resources and help are fairly limited. They want a Vigenere cipher. My code is passing all testing and I thought it was done until I typed "ho1W aRE y0Ou? as the text and "heLLo" as they key. The execution is perfect except for the lowercase u which does not continue through the 'z' - 'a' loop and instead prints ' ' '. The code does does the 'z' to 'a' loop successfully in the 'W' in "how" and 'y' in "you". The key, "heLLo" is does repeat successfully and is not at the end of the strlen when it hits the 'u'. It is also not increasing by 1 on non-alphabetical characters. I'm not sure where to go from this point. Can anyone please offer some suggestions? Thanks!
#include <cs50.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
// Function to get string (text) from user
string Encrypt(void);
int main(int argc, string argv[])
{
// Exits with improper arguement count
if (argc != 2)
{
printf("You must enter one keyword when running the program.\n");
return 1;
}
// Sets key entered for command argument 1
string key = argv[1];
// Checks key to make sure a-z is entered. Exits if not.
for (int i = 0, word = strlen(key); i < word; i++)
{
if (isalpha(key[i]))
{
}
else
{
printf("Only letters are allowed for the key.\n");
return 1;
}
}
string text = Encrypt();
// Secret used to print out final message
char secret = 'a';
// K contorls array place in key
int k = 0;
// If text is entered and alpha: compares text[i] and key[k]
if (text != NULL)
{
for (int i = 0, len = strlen(text); i < len; i++)
{
if (isalpha(text[i]))
{
// Checks k poition to make sure it is within array
if (k == strlen(key))
{
k = 0;
}
// Converts key if text is lowercase
if (islower(text[i]))
{
secret = (((text[i] - 'a') + (key[k] - 'a')) % 26) + 'a';
printf("%c", tolower(secret));
}
// Converts key if text is uppercase
if (isupper(text[i]))
{
secret = (((text[i] - 'A') + (key[k] - 'A')) % 26) + 'A';
printf("%c", toupper(secret));
}
k++;
}
// If not alpha ignores loop and prints text char.
else
{
printf("%c", text[i]);
}
}
}
return 0;
}
string Encrypt(void)
{
printf("Enter your text.");
string text = GetString();
return text;
}
The problem is when it gets to the 'u' in the string, you are on the 'L' in your key. So when this code runs:
secret = (((text[i] - 'a') + (key[k] - 'a')) % 26) + 'a';
By substitution you have:
secret = ((('u' - 'a') + ('L' - 'a')) % 26) + 'a';
Hint: 'L' - 'a' = -21. 'u' - 'a' = 20. Hope you can figure the rest out from here, good luck.

Resources