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.
Related
I have another question about my caesar code-enchipering programs. Here I specified, that I want to take only integer. But somehow it proceed when 2x is inputted in the command line arguments.
Below is the full code... I deeply appreciate it if anyone can help me answer my question.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
//one command line argument with the type int
int main(int argc, string argv[])
{
//main program
if (argc == 2 && isdigit(*argv[1]))
{
int k = atoi(argv[1]); //get Caesar key into a variable
string pltext = get_string("plaintext: "); //getting input for the plain text
char cptext[strlen(pltext) + 1];
for (int i = 0, n = strlen(pltext) ; i < n; i++) //turning pltext to integer
{
if (pltext[i] >= 'a' && pltext[i] <= 'z')
{
cptext[i] = ((pltext[i] - 'a' + k) % 26) + 'a'; //shifting the integer with k (lowercase)
}
else if (pltext[i] >= 'A' && pltext[i] <= 'Z')
{
cptext[i] = ((pltext[i] - 'A' + k) % 26) + 'A'; //shifting the integer with k (uppercase)
}
else
{
cptext[i] = pltext[i]; //other symbol stays
}
}
//print out result
printf("ciphertext: %s\n", cptext);
return 0;
}
//setting condition that : K = + ; if more or less than one, immediate error message, return 1
//if not decimal return = usage ./caesar. key, return value 1 to main
else if (argc != 2)
{
printf("Error 404 : \n");
return 1;
}
else
{
printf("Usage: ./caesar key\n");
return 2;
}
}
The isdigit function only checks the first digit. meaning if I use 2x, itll see 2. what you could do is put a loop, checking the digits in the key given by the user, and see if each one is a digit!
argv[ ] in your program is char * (acc to cs50 header), so first element is a pointer to array of char's , in C programming language if you de-reference a pointer to array you get the first element in your array.
so in your program when you de-reference pointer to string "2x" ex : *argv[1], you get first element of that string (array of chars) which is 2 ! so this returning true (or 1 in this case ) and continuing that conditional statement :)
char * string = "2x";
if (string[0] == *string) print("this is the bug in your program!");
I just have done with Vigenere problem in CS50, but still, there's one wrong only, the non-alphabetic characters, when you write in plaintext anything without spaces, comma, any non-alphabetic, the program will run well, but if you wrote any non-alphabetic character, like space, the next character will take the wrong key, this my code :
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, string argv[])
{
// Make sure there is a command-line argment
if (argc != 2)
{
printf("Error\n");
return 1;
}
// Variables
int key[strlen(argv[1])];
string plaintext;
// Make sure the comman-line argment is Alphabets then make the key
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if (!isalpha(argv[1][i]))
{
printf("Error 2\n");
return 1;
}
if (islower(argv[1][i]))
{
key[i] = argv[1][i] - 'a';
}
else if (isupper(argv[1][i]))
{
key[i] = argv[1][i] - 'A';
}
}
// Ask the user to write the message
plaintext = get_string("plaintext: ");
printf("ciphertext: ");
// Make sure the plaintext doesn't equal NULL
if (plaintext != NULL)
{
for (int i = 0, n = strlen(plaintext); i < n ; i++)
{
// Print in slower case
if (islower(plaintext[i]))
{
printf("%c", (((plaintext[i] + key[i % strlen(argv[1])]) - 'a') % 26) + 'a');
}
// Print in upper case
else if (isupper(plaintext[i]))
{
printf("%c", (((plaintext[i] + key[i % strlen(argv[1])]) - 'A') % 26) + 'A');
}
// Print the non alphabetic
else if (!isalpha(plaintext[i]))
{
printf("%c", plaintext[i]);
}
}
// Print a new line
printf("\n");
}
}
The problem is because you are using the same index for the plaintext and the key in this
for (int i = 0, n = strlen(plaintext); i < n ; i++) loop. The key will advance one position every time plaintext does. Obviously that is not what you want. You need to manage the key index independently of the plaintext index within that loop.
Suggest you rewatch the walkthrough and perhaps write out an example much the way Zamyla does the panda example. And it's never too soon to learn how to use debug50. If I recall correctly, there is a short for it in Week 2.
CS50x has a stack forum dedicated to questions and answers about CS50x and the psets.
#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.
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 ;)
I have been working on this since yesterday and after much struggle have managed to encrypt the message successfully. However, my output is missing spaces.
As I understand it, the reason this is happening is because I'm using isalpha(), isupper() and islower() commands and as a result am ignoring the spaces in the original input.
Can one please help me with how to retain the original spaces and punctuation?
Below is my code- its far from elegant and any comments on style will also be appreciated!
(Also, while there are plenty of questions on Caesar Cipher, none barring one deal with this problem. Since this is my first week programming, I had trouble understanding the syntax in that one.)
There is a blatant mistake in my algorithm which causes it to output the wrong values if given certain arguments. For instance with a k of 13, inputting anything after the 13th alphabet (m, I think) will output something very bizarre.
I will amend this and get back soon! Till then, take my code with a grain of salt!
# include <cs50.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Please enter a valid number of arguments! \n");
return 1;
}
string num = argv[1];
int k = atoi(num);
if (k < 0)
{
printf("Please enter a valid number! \n");
return 1;
}
printf("Please type the message which needs to be encrypted: ");
string p = GetString();
for (int i = 0, n = strlen(p); i < n; i++)
{
int oldletter = p[i];
int result1 = (oldletter + k);
int result2 = (oldletter - 65 + k);
int result3 = (result2) % 26;
int result4 = (oldletter - 97 + k);
int result5 = (result4) % 26;
if (isalpha(p[i]) && isupper(p[i]) && k < 26)
{
printf("%c", result1);
}
if (isalpha(p[i]) && isupper(p[i]) && k >= 26)
{
int result7 = (result3 + oldletter);
printf("%c", result7);
}
if (isalpha(p[i]) && islower(p[i]) && k < 26)
{
printf("%c", result1);
}
if (isalpha(p[i]) && islower(p[i]) && k >= 26)
{
int result8 = (result5 + oldletter);
printf("%c", result8);
}
}
printf("\n");
}
Corrected code, working properly: SPOILERS AHEAD
# include <cs50.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("Please enter a valid number of arguments! \n");
return 1;
}
string num = argv[1];
int k = atoi(num);
if (k < 0)
{
printf("Please enter a valid number! \n");
return 1;
}
printf("Message: ");
string p = GetString();
for (int i = 0, n = strlen(p); i < n; i++)
{
int oldletter = p[i];
int result1 = (oldletter - 65 + k);
int result2 = (result1) % 26;
int result3 = (oldletter - 97 + k);
int result4 = (result3) % 26;
if (isalpha(p[i]) && isupper(p[i]))
{
int result5 = (result2 + 65);
printf("%c", result5);
}
else if (isalpha(p[i]) && islower(p[i]))
{
int result6 = (result4 + 97);
printf("%c", result6);
}
else
{
printf("%c", p[i]);
}
}
printf("\n");
}
A common pitfall I see when people implement this is to go straight to ascii values. Consider making an alphabet array, you can just get the position of your current letter in it, and then determine what the modified letter should be.
Imagine adding a '%' character to this with the ascii solution, you'll end up having a ton of special if's. You can chose in that case to ignore spaces/etc if you like, personally I would add them to the alphabet array so the ciphertext didn't reveal the spaces (giving away hints).
You should probably chain together the if's as else if's, there is no need to evaluate an if condition if one of the previous was already true in your case. This would also allow for a final else case to be executed when isalpha is false like in the case with spaces.
Just change the if conditions to:
if (isalpha(p[i]) && isupper(p[i]) && k < 26)
{
printf("%c", result1);
}
else if (isalpha(p[i]) && isupper(p[i]) && k >= 26)
{
int result7 = (result3 + oldletter);
printf("%c", result7);
}
else if (isalpha(p[i]) && islower(p[i]) && k < 26)
{
printf("%c", result1);
}
else if (isalpha(p[i]) && islower(p[i]) && k >= 26)
{
int result8 = (result5 + oldletter);
printf("%c", result8);
}
else
{
printf("%c", p[i]);
}
I wanna note that your logic is pretty complex and you should also choose better names than the result* variables that you currently use, in programming readability and maintainability are really important. You can easily do assignments without considering them due to the small programs you write there but it's a good habit to get into.
I also took the course (with prior C experience) and uploaded my final solution for you to compare to/improve with once you finished yours. Just a warning, I used a function, not sure if it was already explained before this problem set but should be at least explained soon after. Here is my solution: http://pastebin.com/vJqPY6Ne