Vigenere cipher in cs50 - c

I'm working my way through an online class teaching me how to code. I'm very new to this and have been slowly making my way through this class. I've run into an issue with the vingenere cipher. It doesn't iterate the key through the whole input.
Edit: the key should iterate through the user input and when it reaches the end of the key, loop back and begin again. The key should also skip over any special character(!##" ",etc)
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main (int argc, string argv[])
{
if(argc !=2)
{
printf("please put in command line argument: example - ./vigenere command\n");
return 1;
}
string key = argv[1];
int keylength = strlen(key);
for (int i=0;i<keylength; i++)
{
if(!isalpha(key[i]))
{
printf("please make sure command is letter only. Please no numbers or special characters!\n");
return 1;
}
}
string input = GetString();
for (int i=0, k=0; i<keylength; i++)
{
if(isalpha(input[i]))
{
if(isupper(input[i]))
{
input[i]=((input[i]-'A')+(key[k%keylength]))%26+'A';
}
else
{
if(islower(input[i]))
{
input[i]=((input[i]-'a')+(key[k%keylength]))%26+'a';
}
}
}
}
printf("%s\n",input);
return 0;
}
I know string is not normal, but it's included in the header to help with new students. I guess we learn more as the class progresses.

You didn't change k in your for loop. And indeed I don't think you need k at all. And your loop only iterate through the length of key instead of the length of input.
int inputlength = strlen(input);
for (int i = 0; i < inputlength; ++i) {
if (isupper(input[i]))
input[i] = ((input[i]-'A') + (key[i%keylength])) % 26 + 'A';
/* ... ^ Use i here */
}
Regarding the issue that when the key is b and input is A, you must adjust the key.
input[i] = ((input[i]-'A') + (key[i%keylength]-'a')) % 26 + 'A';
To skip input special characters,
int inputlength = strlen(input);
for (int i = 0, k = 0; i < inputlength; ++i) {
if (isupper(input[i]))
input[i] = ((input[i]-'A') + (key[(k++)%keylength])) % 26 + 'A';
/* ... ^^^ */
else if (islower(input[i]))
input[i] = ((input[i]-'a') + (key[(k++)%keylength])) % 26 + 'a';
}

Related

Problem using C. Program works then acts differently for unknown reason

So in my program I retrieve a command line argument (must be 26 characters with no duplicates) which is used kind of like a rubric for a cypher or something and all these letters are put into an array (i know im not doing it super efficiently).
Following this I prompt for a user to write something and that sentence will in turn change based on what the CLA is inputted as a "cypher" i guess. When i do do this and the cypher is simply just the alphabet (a-z) [therefore should returning the exact same thing written in the prompt] the first couple letters are correct and follow the logic of my code however after getting to the 5th it starts to print out strange random letters for unknown reasons.
ex. hi there how's it going = hi thhrh how's it roisr
plss help :D
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char letters[] = {};
char word[] = {};
// Takes input "Code" and checks if it is suitable (so far) still need to input reminder if no key etc.
int main(int argc, string argv[])
{
if (argc !=2)
{
printf("Missing command-line argument\n");
return 1;
}
else if ((argv[1][1]) == ' ')
{
printf("Usage: ./substitution key");
return 1;
}
else if (strlen(argv[1]) != 26)
{
printf("Key must contain 26 characters.\n");
return 1;
}
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if (isalpha(argv[1][i]) != 0)
{
letters[i] = argv[1][i];
}
else
{
printf("Key must only contain alphabetic characters.\n");
return 1;
}
for (int j = 0; j < i; j++)
{
if (toupper(argv[1][j]) == toupper(argv[1][i]))
{
printf("No Repeat Characters\n");
return 1;
}
}
// confirmed this prints the entire focking CLA printf("%c", letters[i]);
}
string ptext = get_string("plaintext: ");
printf("cyphertext: ");
for (int j = 0; j < strlen(ptext); j++)
{
if (ptext[j] >= 'A' && ptext[j] <= 'Z')
{
int l = ptext[j] - 65;
char z = letters[l];
//printf("%c\n", z);
word[j] = z;
printf("%c", word[j]);
}
else if (ptext[j] >= 'a' && ptext[j] <= 'z')
{
int k = ptext[j] - 97;
char y = letters[k];
word[j] = y;
printf("%c", word[j]);
}
else
{
printf("%c", ptext[j]);
}
}
printf("\n");
}
thats the code!
I've tried debugging and looking into why the value changes however it just suddenly makes letters[k] not equal to e when it should as it is in the array made earlier in the code. I'm not sure what's happening as im pretty sure the code has sound logic
here you have created zero size arrays
char letters[] = {};
char word[] = {};
you need to make them larger. I guess you need
char letters[26] = {};
char word[50] = {};
not quite sure what max size you need tho

Why the loops has a problem (pset2 Substitution)

I have been working CS50X pset2 substitution. I think it almost has done. When I input single char such as A or B D and so on... the ciphertext will get the right result. (e.g A will get J, "B" will get T and D will get E and so on...
However, if I input ABC, the ciphertext will only show J and the other cannot show. What did I do wrong?
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int ciphertext = 0;
//Key
//JTREKYAVOGDXPSNCUIZLFBMWHQ
int main(int argc, string argv[])
{
//Check that program was run with one command-line argument
if (argc == 2)
{
string key = argv[1];
//check the key does it validate
for (int i = 0, n = strlen(key); i < n; i++)
{
string plaintext = get_string("plaintext: ");
printf("ciphertext: \n");
int u = 64;
for (int k = 0, p = strlen(plaintext); k < p; k++)
{
if (isupper(plaintext[k]) != 0)
{
for (int j = 0; j < 26; j++)
{
u = u + 1;
//printf("u is %c\n", u);
if (u == plaintext[k])
{
ciphertext = key[j];
printf("Key is %c\n", key[j]);
printf("Plaintext is %c\n", plaintext[k]);
printf("ciphertext is %c\n", ciphertext);
//break;
}
}
}
else
{
printf("%c", plaintext[k]);
}
}
return 0;
}
}
}
Your problems are logic errors that appear to result from changes you attempted to make to the code that got out of hand. You have unnecessary loops and unused variable in your code. You need to change the scope of the u declaration so it is reinitialized on each iteration. You simply need to remove the for (int i = 0, n = strlen(key); i < n; i++) loop altogether, neither i or n is used. The same for ciphertext and l (ell).
You need to move int u = 64; immediately after if (isupper(plaintext[k])) so it is reset on each iteration.
I suspect all the printf statements were for debugging and you really only want the key output. Putting it altogether, you can rearrange your code to:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main (int argc, string argv[]) {
if (argc == 2) { /* validate key given */
string key = argv[1];
string plaintext = get_string("plaintext: ");
printf("ciphertext: ");
for (int k = 0, p = strlen(plaintext); k < p; k++)
{
if (isupper(plaintext[k]))
{
int u = 64;
for (int j = 0; j < 26; j++)
{
u = u + 1;
if (u == plaintext[k]) {
printf("%c", key[j]);
break;
}
}
}
else
printf("%c", plaintext[k]);
}
putchar ('\n');
}
}
Example Use/Output
$ ./bin/cs50_cypher2 JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext: ABD
ciphertext: JTE
Simplify Your Logic
If you think about what the code above is actually doing, it can be reduced to:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main (int argc, string argv[]) {
if (argc < 2) { /* validate key given */
fputs ("usage: ./program key\n", stderr);
return 1;
}
string key = argv[1];
string plaintext = get_string("plaintext: ");
printf("ciphertext: ");
for (int k = 0, p = strlen(plaintext); k < p; k++)
{
if (isupper(plaintext[k]))
putchar (key[plaintext[k] - 'A']);
else
putchar (plaintext[k]);
}
putchar ('\n');
}
Or if you use a ternary operator, your for loop condenses to simply:
for (int k = 0, p = strlen(plaintext); k < p; k++)
putchar (isupper(plaintext[k]) ? key[plaintext[k] - 'A'] : plaintext[k]);
And since there is no need to call strlen(plaintext), you can eliminate string.h entirely and just loop for (int k = 0; plaintext[k]; k++) since plaintext is a nul-terminated string, your entire program can reduce to:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
int main (int argc, string argv[]) {
if (argc < 2) { /* validate key given */
fputs ("usage: ./program key\n", stderr);
return 1;
}
string key = argv[1];
string plaintext = get_string("plaintext : ");
printf("ciphertext: ");
for (int k = 0; plaintext[k]; k++)
putchar (isupper(plaintext[k]) ? key[plaintext[k] - 'A'] : plaintext[k]);
putchar ('\n');
}
Handling Lowercase Letters
You know your key entered as the first argument is all uppercase. So if you want to output a lowercase key, you must call tolower() on the key after you have applied the offsets. Since for an uppercase letter, you just want to know the offset within the 26-character key to use, you simply find out how many letters your current letter is from 'A' and get that offset in key[], e.g.
key[plaintext[k] - 'A']
For lowercase input, you need to find out the offset in key for the difference in lowercase letters, and then apply tolower() to the key, e.g.
tolower(key[plaintext[k] - 'a'])
See ASCII Table & Description
Putting that altogether, your for loop can be written as:
for (int k = 0; plaintext[k]; k++) {
if (isupper(plaintext[k]))
putchar (key[plaintext[k] - 'A']);
else if (islower(plaintext[k]))
putchar (tolower(key[plaintext[k] - 'a']));
else
putchar (plaintext[k]);
}
Example Use/Output
$ ./bin/cs50_cypher4 JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext : AbZ 50
ciphertext: JtQ 50
Look things over and let me know if you have further questions.

The output isn't what i am expecting, the code isn't printing any error and i dont know whats going wrong

Vigenere cipher
Takes in a text and output a cipher version of that text
the user inputs a keyword in the command line and a text for which the user wishes to encrypt. If you're familiar with the Ceasar cipher it's pretty much the same thing except a small minor change, instead of inputting the actual shift value we instead input a keyword and the letters in the keyword would represent the shift value instead.
#include <cs50.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int shift(char c);
int main(int argc, string argv[])
{
//checks for alphabetic characters
if (argc == 2)
{
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if (isalpha(argv[1][i]) == false)
{
printf("Usage: ./vigenere keyword. \n");
return 1;
}
}
//promting the user for text
string text = get_string("plaintext: ");
printf("ciphertext: ");
//Intializing variables
string key = argv[1];
int l = strlen(key);
for (int j = 0, k = 0, m = strlen(text); j < m; j++)
{
if (islower(text[j]))
{
//lower cap letter enter here
printf("%c", 'a' + ( text[j] - 'a' + shift(key[k % l]) ) % 26);
k++;
}
else if (isupper(text[j]))
{
//Upper caps letter enter here
printf("%c", 'A' + ( text[j] - 'A' + shift(key[k % l]) ) % 26);
k++;
}
else
{
//The rest whatever left enter here
printf("%c", text[j]);
}
}
printf("\n");
return 0;
}
else
{
printf("Usage: ./vigenere keyword. \n");
return 1;
}
}
//This function calculates the shift value per character
int shift(char c)
{
return ( islower(c) - 'a' );
}
the problem is that the output isn't what I expect it to be. For instance, if the keyword is 'b' and the text is 'hello' the output must be 'ifmmp' but it's not. I don't know what goes wrong.

Struggling with Vigenere! (In C) invalid operands to binary expression ('int *' and 'int') and other things

we are now working with caesar, vigenere. I mannaged to finish caesar but vigenere is not working as good. C gives me back: invalid operands to binary expression ('int *' and 'int'). I'm not sure what the program means exactly and what is wrong with my code. Can somebody help me out or give me advice? I think it's because I can not count with the different types of numbers? I'm not sure!
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("You have to input a key, try again!\n");
return 1;
}
string key = argv[1];
int keylength = strlen(key);
for (int i = 0; i < keylength; i++)
{
if (!isalpha(argv[1][i]))
{
printf("Please insert letters, nothing else\n");
return 1;
}
}
printf("plaintext: ");
string plain = get_string();
int keycipher[keylength];
for(int i = 0; i < keylength; i++)
{
keycipher[i] = toupper(key[i]) - 65;
}
if (plain != 0)
{
printf("ciphertext: ");
int i;
for (i = 0, keylength = strlen(key); i < keylength; i++)
{
if (isupper(plain[i]))
{
printf("%c", (plain[i] - 65 + keycipher) % 26);
}
else if (islower(plain[i]))
{
printf("%c", (plain[i] - 97 + keycipher) % 26);
}
else if (plain[i] == ' ')
{
printf(" ");
}
else
{
printf("%c", plain[i]);
}
}
}
return 0;
}
As mentioned in the comments, you have to use + keycipher[index] instead of + keycipher. This is because keycipher is an array and you need to use only one of the elements of the array at a time.
Further, you will need 2 counter variables instead of just one in the part where you find the ciphertext. This is because you need to increment the plaintext index and the key index in every iteration, but once the plaintext length crosses the key length, you will need to reset the key index.
If you check the specifications, you will understand what I am saying about using 2 counters.

fgets() creating another copy of string at time of input?

I'm wondering what the error is here? I'm making a causer cipher, and it works fine but when I print the encrypted message at the end, it reprints the unencrypted message. When I comment out and set message to a sentence, it works fine. But when I get a string using fgets it creates another copy. Thanks in advance!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
char message[100];
printf("Please Enter a message to be Encrypted: ");
fgets(message, 100, stdin);
unsigned char caeser[] = "";
int j = 0;
if(argc >= 2)
{
int k = atoi(argv[1]);
for(int i = 0, n = strlen(message); i < n; i++)
{
printf("%i\n",n);
if(!isspace(message[i]))
{
if(islower(message[i]))
{
j = message[i] - 97;
caeser[i] = 97 + ((j + k) % 26);
}
if(isupper(message[i]))
{
j = message[i] - 65;
caeser[i] = 65 + ((j + k) % 26);
}
}
else
{
caeser[i] = ' ';
}
}
printf("%s\n", caeser);
}
}
There is no issue with fgets() here. The real issue here is you did not define the array caeser properly.
You need to provide the size explicitly if you don't initialize with enough initializers at definition time. A compile-time array won't magically expand based on the usage. You have to pre-decide the size, either by mentioning it explicitly or by initializing it with enough number of initalizer elements.
Here, something like
unsigned char caeser[100] = {0};
would do the job for you.

Resources