Okay. So, I need to make a Vigenere cypher. When the text and key are both upper case of both lower case, the code compiles fine. But when the text and the key differ from case, the code is not working. Then it doesn't print anything. For example when the key is: aaAA. And the text is aBcD. The outcome is: aD. Can someone give me a hint, please? :)
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
int main (int argc, string argv[])
{
string key = argv [1]; //argv [1] is the key. 0 is compile program
{
if (argc != 2)
{
printf ("Please give one key: "); //if there are more or less then 2 argc, then have to try again
}
for (int j = 0, n = strlen (key); j < n; j++)
if (!isalpha (key [j]))
{
printf ("Please give a key in alphabetic characters: ");
//key must be alphabetic. For loop to check every character of the key.
return 1;
}
}
string text = GetString(); //Get secret message from user
int j = 0;
for (int i = 0, n = strlen (text); i < n; i++)
{
if (isupper (text [i]))
{
if (isupper (key [j]))
{
/*Minus 65 to make count till 26 from text and key. Use modulo to wrap around key. And modulo to wrap around alphabet.
Plus 65 to go to correct ASCII character. */
int u = ((((text [i] - 65) + (key [j % strlen (key)] - 65)) % 26) + 65);
printf ("%c", u);
}
}
else if (islower (text [i]))
{
if (islower (key[j]))
{
int l = ((((text [i] - 97) + (key [j % strlen (key)] - 97)) % 26) + 97);
printf ("%c", l);
}
}
else if (islower (text [i]))
{
if (isupper (key[j]))
{
int lu = ((((text [i] - 97) + (key [j % strlen (key)] - 65)) % 26) + 97);
printf ("%c", lu);
}
}
else if (isupper (text [i]))
{
if (islower (key[j]))
{
int ul = ((((text [i] - 65 + (key [j % strlen (key)] - 97)) % 26) + 65);
printf ("%c", ul);
}
}
else
{
// When character is non alphabetic print it in its original form.
printf ("%c", text [i]);
}
j++;
}
{
printf ("\n");
return 0;
}
}
The problem is in your if, else-if, else-if... statements. The reason is cause if isupper(text[i]) returns true, and if isupper(key[j]) returns false it will never evaluate the else if statements. You should do this
if( isupper(text[i])){
if(isupper(key[j])){ // Both upper
//do stuff
}
else if(islower(key[j])){ //Here key is lower and text is upper
//do stuff
}
}
else if (islower(text[i])){
if (islower(key[j])){ //Both lower
//do stuff
}
else if(isupper(key[j])){ //Key upper and text lower
//do stuff
}
}
else{//It's not alpha
//do stuff
}
/***************NEW********************/
j = j%strlen(key); //I suggest using this at the end of the loop to avoid key[j] to go out of it's bounds
// j = (j==strlen(key)) ? 0 : j; //Another alternative
Also, I think you shouldn't be increasing j if the character is not alpha
Related
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 have made program which encrypts and decrypts Vigenere's cipher but I have several problems.
Here is one: First letter of sentence is encrypted incorrectly.
Second one: After sentence I have letter K. I think that's because of space but I don't know how to fix it.
And third problem: There are no spaces in encrypted sentence I know ages ago when Vigenere's cipher was used there were no spaces but I would like to have groups of 5 letters if that's possible.
Here's my code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char **argv) {
char message[100];
int choice;
int i, j;
char pass[33];
int value;
char repeat = 1;
while (repeat == 1) {
printf("Enter operation\n");
printf("Encrypt - 1 \n");
printf("Decrypt - 2\n");
scanf("%d", &choice);
if (choice == 1) {
printf("Please enter message to encrypt\n");
while (getchar() != '\n');
fgets(message, 100, stdin);
printf("Enter password\n");
scanf("%s", &pass);
for (i = 0, j = 0; i < strlen(message); i++, j++) {
if (message[i] == ' ')
continue;
if (j >= strlen(pass)) {
j = 0;
}
if (!isupper(message[i])) {
value = (((message[i]) - 97) + ((pass[j]) - 97));
}
if (!islower(message[i])) {
value = (((message[i]) - 65) + ((pass[j]) - 65));
}
printf("%c", 97 + (value % 26));
}
printf("\nWould you like to repeat? [1/0]\n");
scanf("%d", &repeat);
} else
if (choice == 2) {
printf("Enter message do decrypt\n");
while (getchar() != '\n');
fgets(message, 100, stdin);
printf("Zadejte heslo\n");
scanf("%s", &pass);
for (i = 0, j = 0; i < strlen(message); i++, j++) {
if (message[i] == ' ')
continue;
if (j >= strlen(pass)) {
j = 0;
}
if (!isupper(message[i])) {
value = (((message[i]) - 96) - ((pass[j]) - 96));
}
if (!islower(message[i])) {
value = (((message[i]) - 64) - ((pass[j]) - 64));
}
if (value < 0) {
value = value * -1;
}
printf("%c", 97 + (value % 26));
}
printf("\nWould you like to repeat? [1/0]\n");
scanf("%d", &repeat);
}
}
return (EXIT_SUCCESS);
}
[
The main problem in your code is you apply the translation to characters with incorrect tests: you should translate uppercase letters is you have indeed an uppercase letter, not if you don't have a lowercase character. As coded, non letters are translated twice.
Change the code to:
if (islower((unsigned char)message[i])) {
value = (((message[i]) - 'a') + ((pass[j]) - 'a'));
}
if (isupper((unsigned char)message[i])) {
value = (((message[i]) - 'A') + ((pass[j]) - 'a'));
}
Also make sure you use character constants instead of hard-coded ASCII values and make the password lowercase.
In the deciphering case, the offsets seem incorrect. You should be using 'A' and 'a' too.
First things first, the message corruption. If you add a few printf() statements in the loop that is doing the encryption, you should be able to get an idea what is going wrong. You can always comment them out, or remove them altogether, anytime later.
That K on the end could be the encrypted \n that would have been read in with the message.
To display the encrypted message in groups of five characters, keep a count of how many characters you have actually displayed (make sure the instruction to increase the count is located where it will get skipped if the character is not displayed); and when this reaches 5, display a space and reset the counter to zero.
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.
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.