Basically I have to create a caesar cipher, which is solely replacing each letter given with a letter that is int 'k' away. This takes 2 command line arguments: './caesar' and 'k', which given by the user. it works fine; but has one issue:
it encrypts "BARFOO" as "EDUIRR" using 3 as key which is correct
encrypts "BaRFoo" as "FeVJss" using 4 as key which is correct
BUT it does not encrypt "barfoo" as "onesbb" using 65 as key, it encrypts it as "oonneess|bb|bb" .
Please notice the punctuation; the caps and so on.
See the problem here? it also does this for other random words; it repeats letters. Help me....
PS: I am extremely new to programming, as you can see in my code, so please try to explain in english!
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
string s;
//int d;
int c;
int a;
if(argc != 2)
{
printf("Please run with a command line argument.");
return 1;
}
else
{
s = GetString();
}
int k = atoi(argv[1]);
for(int i = 0; i < strlen(s); i++)
{
a = s[i];
if(a<'A'||a>'z')
{
printf(" ");
}
else
{
if(a>='A'&&a<='Z')
{
c = a+k;
while(c>'Z')
{
c = 'A'+(c-'Z')-1;
printf("%c", c);
}
if(c<='Z')
{
printf("%c", c);
}
}
else if(a>-'a'&&a<='z')
{
c = a+k;
while(c>'z')
{
c = 'a'+(c-'z')-1;
printf("%c", c);
}
if(c<='z')
{
printf("%c", c);
}
}
}
}
printf("\n");
}
You can try to take k%26 since it should wrap around the characters of alphabet.
That should solve your problem.
You should try this way. Take the case of uppercase letters. First get the index from the letter 'A'.
index = a - 'A';
Then add the value in the variable k and get the remainder when divided with 26.
modified_index = ( index + k ) % 26;
Now to get the desired letter just add it with 'A'.
c = 'A' + modified_index;
Only adding k%26 will not help as that way 'z' with increment 1 will be turned into { which is wrong.
Also, if you just add value in k with the letter denoted by variable a it might cross the limit of ASCII characters as Joulin mentioned.
Related
Going through the CS50 exercise Caesar, and I'm still very new to C. My code to validate the key is all working, but as i try to convert from plaintext to the cipher text I keep getting caught up with the conversion form the ASCII number to the char. For example if I run the plaintext with the argv[1] as 27 I get /023/024. I recognize the math is off and if anyone has any pointers about that that would be great, but the main thing I'm curious about is why when I try to assign f into input[i] I get this "/000" type format.
string input = get_string("plaintext: ");
int len = strlen(input);
int i = 0;
while (input[i] != '\0')
{
int mod = atoi(argv[1]);
int t = input[i] + mod;
int f = t % 26;
input[i] = f;
i++;
}
printf("ciphertext: %s\n", input);
Your implementation of doing the %26 is wrong because when you do t % 26 you will get a letter but you have forgotten to add the ASCII value if it is a small of a larger one.
you must change your code to
Here is my code which I had used,
It is correct and hope it helps you...
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if (argc != 2) //checking if there are 2 arguments while calling the main method the first one here will be "./caesar" and the second will be the input given
{
printf("Wrong command\n");
return 1;
}
int k = atoi(argv[1]);//converting string input to integer
string a = argv[1];
for (int i = 0; i < strlen(a); i++)//checking if the input is a number for each character
{
if (a[i] < '0' || a[i] > '9')
{
printf("False\n");
return 1;
}
}
if (k < 0)
{
printf("Wrong command\n");
return 1;
}
else
{
//if everything is successful we ask for the plaintext
string code = get_string("plaintext: ");
//we just print 'ciphertext' and not the 'ciphered text'
printf("ciphertext: ");
for (int i = 0, n = strlen(code); i < n; i++)
{
if (islower(code[i]))
{
printf("%c", (((code[i] + k) - 97) % 26) + 97);
}
else if (isupper(code[i]))
{
printf("%c", (((code[i] + k) - 65) % 26) + 65);
}
else
{
printf("%c", code[i]);
}
}
printf("\n");
return 0;
}
}
Your mistake is that, you are not checking if it is an upper case or lower case and you are not subtracting 97 or 65 before doing the modulus...
If any other doubts, you are free to ask.
Here is the result after running check50 for the 2021 version
:) caesar.c exists.
:) caesar.c compiles.
:) encrypts "a" as "b" using 1 as key
:) encrypts "barfoo" as "yxocll" using 23 as key
:) encrypts "BARFOO" as "EDUIRR" using 3 as key
:) encrypts "BaRFoo" as "FeVJss" using 4 as key
:) encrypts "barfoo" as "onesbb" using 65 as key
:) encrypts "world, say hello!" as "iadxp, emk tqxxa!" using 12 as key
:) handles lack of argv[1]
All successful
I'm doing the CS50 Caesar problem and for the most part, my code works. I am not able to pass one of the check50 tests - my code does not handle non-numeric keys, and it timed out while waiting for the program to exit.
I have tried utilizing isdigit but it does not seem to work.
The check50 tests results copied pasted below:
:) caesar.c exists.
:) caesar.c compiles.
:) encrypts "a" as "b" using 1 as key
:) encrypts "barfoo" as "yxocll" using 23 as key
:) encrypts "BARFOO" as "EDUIRR" using 3 as key
:) encrypts "BaRFoo" as "FeVJss" using 4 as key
:) encrypts "barfoo" as "onesbb" using 65 as key
:) encrypts "world, say hello!" as "iadxp, emk tqxxa!" using 12 as key
:) handles lack of key
:( handles non-numeric key
timed out while waiting for program to exit
:) handles too many arguments
#include <stdio.h>
#include <cs50.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main (int argc, string argv[])
{
if (argc == 2 && (isdigit(argv[1] !=0))
{
int k = atoi (argv[1]); // convert string to int
string s = get_string ("plaintext: "); // obtain text
printf("ciphertext: ");
for (int i = 0; i < strlen(s); i++) // text loop
{
if (s[i] >= 'a' && s[i] <= 'z')
{
printf("%c", 'a' + ((s[i] - 'a') + k) % 26);
}
else if (s[i] >= 'A' && s[i] <= 'Z')
{
printf("%c", 'A' + ((s[i] - 'A') + k) % 26);
}
else
{
printf("%c", s[i]);
}
}
printf("\n");
return 0;
}
else
{
printf("./caesar key\n");
}
return 1;
}
I guess that the timing out is happening because your program is waiting for plaintext while the judge is not giving that because it excepts your program to exit right after giving non-numeric key.
You can use strtol(), which accepts a pointer to pointer to character and saves position of first invalid character.
Then, you can check if the input is numeric by checking if the returned pointer is pointing at the terminating null charcter.
char* p;
int k = (int)strtol (argv[1], &p, 10);
if (*p != '\0') {
puts("non-numeric key");
return 1;
}
Just loop through every digit of argv[1] and check if it's an integer.
for (int i = 0; i < strlen(argv[1]); i++)
{
if (isdigit(argv[1][i]) == 0)
{
return 1;
}
}
I am extremely new to programming and I'm having some difficulties with Vigenere in C from the edX course CS50. I have broken the problem down into uppercase letters and lowercase letters and I am only trying to solve the uppercase letter problem right now. I am using the word 'panda' as my key and 'ILIKEYOU' as the plaintext. When I run the program, the first letter corresponds to the letter I'd expect it to be (23=X). After that, the program just seems to spit out random numbers for the remaining 7 letters. I haven't converted back to ASCII since I'm having so many problems with my code. Any ideas what is going on? Thank you all so much for the help :)
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
// Print error message if the user imput is executed without any
command-line arguments or with more than one command-line argument
if (argc != 2)
{
printf("Usage: ./vigenere k\n");
return 1;
}
// Access key
string key = argv[1];
// Convert the array from a string to an int
int letter;
letter = atoi(argv[1]);
// Print error message if the user imput is one command-line argument
and contains *any* non-alphabetical character(s)
for (int c = 0; c < strlen(key); c++)
{
if (!isalpha (key[c]))
{
printf("Usage: ./vigenere k\n");
return 1;
}
}
// Prompt the user for a string of plaintext
string p;
p = get_string("plaintext:");
//Print ciphertext
printf("ciphertext: ");
// Accessing each character in the plaintext
for (int i = 0, n = strlen(p); i < n; i++)
{
// Shift letters only in the plaintext
if (isalpha(p[i]))
{
// Convert plaintext and key to ASCII capital letters to
alphabetical index
// Encipher capital letters in plaintext and print
int c = 0;
if (isupper(p[i]))
{
printf("%i\n", ((p[i] - 65) + (toupper(key[c]) - 65)) % 26);
}
}
}
Needs few modifications -
int index_key = 0;
int shift= 0;
int key_len = strlen(key);
for (int i = 0, n = strlen(p); i < n; i++)
{
// Shift letters only in the plaintext
if (isalpha(p[i]))
{
// Convert plaintext and key to ASCII capital letters to
//alphabetical index
// Encipher capital letters in plaintext and print
if (isupper(p[i]))
{
shift = ((p[i] - 'A') + (toupper(key[index_key % key_len]) - 'A')) % 26;
index_key++;
printf("%c", p[i] + shift);
}
}
}
The output is wonky when I use numbers 4-22, and I can't figure out why. Help would be greatly appreciated, and I would also like to know why this does not work.
#include<cs50.h>
#include<stdio.h>
#include<string.h>
int main(void)
{
int shifts;
int enc;
printf("What is your message ");
string message = get_string();
printf("By how many letters do you want to shift?");
scanf("%d",&shifts);
for(int i=0;i<strlen(message);i++)
{
enc=((message[i] - 89)+shifts)%26;
printf("%c",enc + 89);
}
printf("\n");
}
Inside the for loop you should check whether the character is uppercase, lowercase or neither. The number 89 is also wrong, that is 'Y', what you probably want is 65 or 97, 'a' and 'A' respectively. The for loop should be changed to be something like:
#include <ctype.h> // For isupper()/islower()
for(int i = 0; i < strlen(message); i++)
{
if(isupper(message[i])) { // Check if current char is uppercase
enc=((message[i] - 'A')+shifts)%26; // Apply cipher
printf("%c",enc + 'A');
} else if(islower(message[i])) { // Check if current char is lowercase
enc=((message[i] - 'a')+shifts)%26; // Apply cipher
printf("%c",enc + 'a');
} else { // Print char without change
printf("%c", message[i]);
}
}
Note the use of 'A' and 'a' instead of 65 and 97, these will translate to the corresponding integer literals at compile time. There are other ways to write this, this probably isn't even the most clean way to do it (multiple printf()s for example), but it should illustrate how this works and should be done.
I was making a program for Vigenere cipher. I made the program print the cipher text successfully. But, I can't loop the key. so if my key was 'abc' and my plain text was hello, it should print 'hfnlp' not 'hfn'.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if(argc != 2)
{
printf("\aError\n");
return 1;
}
else
{
string a = argv[1]; // converts argv[1]
printf("plaintext: ");
string b = get_string(); // takes the plaintext
printf("ciphertext: ");
for(int c = 0, d = strlen(a); c < d; c++)
{
for(int e = 0, f = strlen(b); e < f; e++)
{
if(islower(a[c]))
{
printf("%c\n", b[e] + ( (a[c] - 97) % 26) ); // works for lowercase letters
return 0;
}
else if(isupper(a[i]))
{
printf("%c\n", b[e] + ( (a[c] - 65) % 26) ); // works for uppercase letter
}
else
{
printf("%c", b[e]); // works for non alphabetical inputs
}
if(true)
break;
}
}
printf("\n");
}
}
Your choice of single-letter variable names is odd; it makes it harder to work with your code. I'm not a fan of long names either, but intermediate length variable names (2-8 characters — except for some stylized single-letter names (c, i, j, k, p, s) — is typically appropriate).
You've got trouble because if your key is 6 characters and your string is 24 alphabetic characters, you'll attempt output 144 alphabetic characters because of the loop structure. You only need a single loop that iterates over the characters in the plain text. You have a separate variable that cycles over the length of the key, resetting back to the start when it runs out. In this code, the key length is in keylen (you used d) and the offset (index) into the key is in keyoff (you used c) — but the key is still in a because that's what you used. Left to my own devices, I'd probably use text (or maybe plain) in place of b, textlen in place of f, and I'd use i instead of e for the loop variable. If I wanted to use short indexes, I might use k instead of keyoff. I might also edit the string in situ and print the whole string at the end.
This code also ensures that the alpha characters in the key are in lower case. It doesn't ensure that the key is all alpha; it arguably should and it would be trivial to do so since the key is scanned anyway. As it stands, it is a case of GIGO — garbage in, garbage out.
The code converts the input letter (a-z or A-Z) into an 'offset into the alphabet' by subtracting a or A, converts the key letter into an offset into the alphabet, adds the two offsets modulo 26 (number of letters in the alphabet), and converts the offset back into a letter of the appropriate case.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if (argc != 2 || strlen(argv[1]) == 0)
{
fprintf(stderr, "Usage: %s key < text\n", argv[0]);
return 1;
}
string a = argv[1];
int keylen = strlen(a);
for (int i = 0; i < keylen; i++)
a[i] = tolower((unsigned char)a[i]);
printf("key: %s\n", a);
printf("plaintext: ");
string b = get_string();
printf("ciphertext: ");
int keyoff = 0;
// Step through each character of the plain text. Encrypt each
// alpha character with the (lower-case) key letter at a[keyoff],
// incrementing keyoff. Don't increment key offset when processing
// non-alpha data.
for (int e = 0, f = strlen(b); e < f; e++)
{
if (islower(b[e]))
printf("%c", ((b[e] - 'a') + (a[keyoff++] - 'a')) % 26 + 'a');
else if (isupper(b[e]))
printf("%c", ((b[e] - 'A') + (a[keyoff++] - 'a')) % 26 + 'A');
else
printf("%c", b[e]);
if (keyoff >= keylen)
keyoff = 0;
}
printf("\n");
return 0;
}
When compiled to the program vc41 and run, it produces, for example:
$ vc41 abcdef
key: abcdef
plaintext: The quick brown Fox jumped over the lazy Dog.
ciphertext: Tig tyncl dusbn Gqa nzmqgg saes vki qaaa Gsl.
$
I generated an 8-letter random key (it was GZlfmTMk) and ran the code on a number of 'complete alphabet' strings:
$ vc41 GZlfmTMk
key: gzlfmtmk
plaintext: Pack my box with five dozen liquor jugs.
ciphertext: Vznp yr nyd vtyt yufk czeqg xswtzw vnsc.
$ vc41 GZlfmTMk
key: gzlfmtmk
plaintext: The five boxing wizards jump quickly.
ciphertext: Zgp kuoq luwtss pujgqox vnyz wtthwek.
$ vc41 GZlfmTMk
key: gzlfmtmk
plaintext: How vexingly quick daft zebras jump.
ciphertext: Nnh aqquxmkj vgbou jzqy lxnbgr uzyi.
$ vc41 GZlfmTMk
key: gzlfmtmk
plaintext: Bright vixens jump; dozy fowl quack.
ciphertext: Hqtltm hsddyx vnyz; jnkd rhiv wtlhw.
$ vc41 GZlfmTMk
key: gzlfmtmk
plaintext: The quick brown fox jumps over the lazy dog.
ciphertext: Zgp vgbou hqzbz yah ptxue hhox ssj xtli jnr.
$
(I'll note in passing that on a Mac running macOS Sierra 10.12.6 using GCC 7.1.0, this code links without including the (new) CS50 library — there is a system function get_string() that has a different interface to the CS50 version that satisfies the reference but crashes the program. However, it isn't documented by man get_string, so I'm not sure what the system function of that name actually does; I haven't chased it more actively, or found out how extensive the problem is. That caused me a headache that the old CS50 library didn't. Grumble…)
fix like this
#include <stdio.h>
#include <ctype.h>
#include <cs50.h>
int main(int argc, string argv[]){
if(argc != 2 || !*argv[1]){
printf("\aError:The number of command arguments is incorrect.\n");
printf("Usage: %s key_string\n", argv[0]);
return 1;
}
//Since it is `return 1;` in the if-statement,
//the else clause is unnecessary (unnecessarily deepening the nest)
string key = argv[1];//It does not convert.
size_t i, key_len;
unsigned char curr_char;
for(i = 0; (curr_char = key[i]) != '\0'; ++i){
if(!isalpha(curr_char)){
printf("\aError:Only the alphabet can be specified as the key.\n");
return 1;
}
key[i] -= islower(curr_char) ? 'a' : 'A';//Convert to Deviation
}
key_len = i;
i = 0;
printf("plaintext : ");
string plain = get_string();
printf("ciphertext: ");
for(size_t j = 0; (curr_char = plain[j]) != '\0'; ++j){//Scan of plain text should be the main loop.
if(isalpha(curr_char)){
char base_char = islower(curr_char) ? 'a' : 'A';//Avoid using magic numbers
putchar(base_char + (curr_char - base_char + key[i]) % 26);//Make the same process one
if(++i == key_len)
i = 0;//reset key index
} else {
putchar(curr_char);//non alphabetical inputs
}
}
printf("\n");
free(plain);
}