I've been working on cs50 pset2, and I thought I had the vigenere cipher down after working on it for a few days. This code is meant to take an alphabetical argument(argv[]) given by the user, and use that as a key to crypt a phrase given by the user(string) by its number in the alphabetical index. For example, if you give the argument 'abc' and the string 'cat' then the output should be 'cbv'(a moving 0, b moving 1, c moving 2) The argument should also wrap around so that if the string is longer, the argument will wrap to its first character and continue until the string has ended.
This is what I have for code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if(argc != 2)
{
printf("Try again\n");
return 1;
}
string k = (argv[1]);
int klen = strlen(k);
for(int x = 0; x < klen; x++)
{
if(isalpha(k[x]))
{
if(isupper(k[x]))
{
k[x] = tolower(k[x]);
}
k[x] -= 'a';
}
else
{
printf("Try again\n");
return 1;
}
}
string code = GetString();
int clen = strlen(code);
for(int a = 0, b = 0; a < clen; a++)
{
if(isalpha(code[a]))
{
int key = k[b%klen];
if(isupper(code[a]))
{
printf("%c", (((code[a] - 'A') + key)%26) + 'A');
b++;
}
else
{
printf("%c", (((code[a] - 'a') + key)%26) + 'a');
b++;
}
}
else
{
printf("%c", code[a]);
}
}
printf("\n");
}
The code seems to work for the length of the key +1.
For example,
I input an argument of 'aaaa'
Then input a string of 'bbbbb'
and receive 'bbbbb' correctly.
However, if I input the same 'aaaa'
Then input a string longer than the key +1 'bbbbbbb'
I receive 'bbbbbNN'
I believe I have an issue with my order of operations but have tried moving parenthesis around to no avail. I was hoping someone could point me in the right direction as to why my key isn't wrapping properly.
Your biggest risk with code like this is all the similar, repetitive clauses. A bug in just one is hard to track done. And doing any processing on the key, while processing the code, is just inefficient.
Here's a rework that completely processes the key before processing the code and tries to get the processing down to just one case. See if it works any better for you:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
fprintf(stderr, "Try again\n");
return EXIT_FAILURE;
}
string key = strdup(argv[1]);
size_t key_length = strlen(key);
for (int x = 0; x < key_length; x++)
{
if (isalpha(key[x]))
{
if (isupper(key[x]))
{
key[x] = tolower(key[x]);
}
key[x] -= 'a';
}
else
{
fprintf(stderr, "Try again\n");
return EXIT_FAILURE;
}
}
string code = GetString();
int code_length = strlen(code);
for (int a = 0, b = 0; a < code_length; a++)
{
if (isalpha(code[a]))
{
int start = isupper(code[a]) ? 'A' : 'a';
printf("%c", (((code[a] - start) + key[b++ % key_length]) % 26) + start);
}
else
{
printf("%c", code[a]);
}
}
printf("\n");
free(key);
return EXIT_SUCCESS;
}
Related
I'm a newbie, so apologies if I don't explain myself well. If it helps, I'm doing this for the Caesar problem set as part of the Harvard CS50x OpenCourseWare.
I'm trying to convert user generated plain text to cipher text using a simple key. To accomplish this I'm attempting to use a wraparound counting formula in my last function. However, sometimes I get blanks that print out instead of the new characters... Help!
EDIT: I'm using a key of 5 and the plaintext "Helloz!" to test. Expect to see Mjqqte!
instead am seeing blank spaces.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
int convert(string n);
string k, text;
char text;
int r, c, t,x;
bool validate(string n);
//int encrypted(string n);
int main(int argc, string argv[])
{
//accept single command-line argument, non negative integer, k with appropriate error
k = argv[1];
if (argc > 1 && argc <= 2)
{
//printf("Success\n%s\n", argv[1]);
// print individual characters of argv[i]
validate(k);
}
else //if wrong input then print error message and main should return 1
{
printf("Usage: ./caesar key\n");
return 1;
}
text = get_string("plaintext:");
t = atoi(k);
printf("%i\n", t);
convert (text);
printf("\n");
}
//output "ciphertext:" without a newline, with the characters roated by k positions
//after output, print a newline and exit by returning 0 from main
bool validate(string n)
{
for (int i = 0; k[i] != '\0'; i++)
{
if (48 <= k[i] && k[i] <= 57)
{
//printf("%c\n", k[i]);
}
else
{
printf("./caesar key\n");
return 1;
// save for later: printf("%s \n", k);
}
}
return r;
}
int convert(string n)
{
//if fits within a range, Reads individual characters
for (int i = 0; i < text[i]; i++)
{
if (isalpha(text[i]))
{
x = text[i];
//printf("%i\n", x);
c = (x+t) % 26;
// printf("%i\n",c);
printf("%c", c);
}
else
{
printf("%i", text[i]);
}
}
return 0;
}
Here's an implementation that could work for you:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
void convert(char *text, unsigned char k) {
for (unsigned int i = 0; i < strlen(text); i++) {
if (isalpha(text[i])) {
// Contains the 3 leftmost bits, containing the uppercase/lowercase part.
char c = (text[i] / 32) * 32;
// Perform the shifting with modulo on the alphabetic index of the letter.
text[i] = c + ((text[i] % 32) + k) % 26;
}
}
}
int main(int argc, char *argv[]) {
unsigned char k = strtol(argv[1], NULL, 10);
char text[64];
printf("Using key %d.\n", (int) k);
printf("Plaintext: ");
fgets(text, 64, stdin);
// Remove newline.
text[strlen(text) - 1] = 0;
convert(text, k);
printf("Ciphertext: %s.\n", text);
return 0;
}
Test run:
>>> cipher 4
Using key 4.
Plaintext: Test mE Right Away!!1
Ciphertext: Xiwx qI Vmklx Eaec!!1.
I am new here and working on the second homework Caesar of cs50, it seems most of my review is correct except the last one -- I cannot handle the situation of lacking argv[1], which means if I only type ./caesar, it will return segmentation fault. I am wondering why this code if (argc != 2) cannot return 0 when argc == 1, however it works when argc > 1, I find that is weird. Can anyone help me?? Thanks in advance!
# include <stdio.h>
# include <cs50.h>
# include <string.h>
# include <ctype.h>
# include <math.h>
# include <stdlib.h>
int check_the_key(int argc, string y);
int main(int argc, string argv[])
{
string x = argv[1];
int y = argc;
int k = check_the_key(y, x);
if (k == 0)
{
printf("ERROR!!!!!\n");
return 1;
}
else
{
// printf("The key is %i\n", k);
string text = get_string("Input your text:");
int i;
int n;
printf("ciphertext: ");
for (i = 0, n = strlen(text); i < n; i++)
{
if (islower(text[i]))
{
printf("%c", (text[i] - 97 + k) % 26 + 97 );
}
else if (isupper(text[i]))
{
printf("%c", (text[i] - 65 + k) % 26 + 65);
}
else
{
printf("%c", text[i]);
}
}
printf("\n");
return 0;
}
}
int check_the_key(int argc, string y)
{
int number = argc;
string key = y;
int numberkey = atoi(key);
if (argc != 2)
{
return 0;
}
else
{
if (numberkey > 0)
{
return numberkey;
}
else
{
return 0;
}
}
}
I know what is going on! Because I need to pass some value into atoi(), if I only call ./caesar, there is no value I can pass into atoi(), so it causes segmentation fault. Which means I need to change code order slightly, put int numberkey = atoi(key); inside the else loop. So the code will run if (argc != 2) first, if no, then go to the next step! Here is the code after change.
int check_the_key(int argc, string y)
{
int number = argc;
string key = y;
if (argc != 2)
{
return 0;
}
else
{
int numberkey = atoi(key);
if (numberkey > 0)
{
return numberkey;
}
else
{
return 0;
}
}
}
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.
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.
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';
}