I'm trying to do the CS50 Vigenere exercise.
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, string argv[])
{
//Check for 2 command line arguments
if (argc != 2)
{
printf("Nah bro, you gotta have 2 arguments.\n");
return 1;
}
//Check is alpha
else {
for (int i = 0; i < strlen(argv[1]); i++)
{
if (isalpha(argv[1][i]) == 0)
{
printf("Nah bro, u gots to use letters.\n");
return 1;
}
}
}
//Prompt user to input text
printf("plaintext: ");
string p = get_string();
//Cipher
printf("ciphertext: ");
string k = argv[1];
int cipherlen = strlen(k);
//Cycle through key letters
for (int i = 0, j = 0, n = strlen(p); i < n; i++)
{
if (isalpha(p[i]))
{
if (isupper(p[i]))
{
printf("%c", ((p[i] - 65) + (k[(j % cipherlen)]) - 65) % 26 + 65);
j++;
}
else if (islower(p[i]))
{
printf("%c", ((p[i] - 97) + (k[(j % cipherlen)]) - 97) % 26 + 97);
j++;
}
else
printf ("%c", p[i]);
}
}
printf("\n");
return 0;
}
Here are my error codes according to the check:
https://cs50.me/checks/a56bc9325327035cb0e8d831693c9805c4b6468b
I understand my problem has to do with cycling through each letter but not applying it to spaces or symbols. I've tried using an if (isalpha) statement and an else printf(" ") but it doesn't work well for numbers or symbols. I figured adding j++ would iterate only through alpha characters but it doesn't seem to help.
Is there something here super plain I'm missing?
The basic structure of your code looks OK.
I see three problems with it:
All of your printfs are guarded by the if (isalpha(p[i])) check, so your program never outputs anything if the plaintext character is not alphabetic (it should output the character unchanged instead). The fix for this is simple; just remove the outer if (...) in the loop:
for (int i = 0, j = 0, n = strlen(p); i < n; i++)
{
if (isupper(p[i]))
{
printf("%c", ((p[i] - 65) + (k[(j % cipherlen)]) - 65) % 26 + 65);
j++;
}
else if (islower(p[i]))
{
printf("%c", ((p[i] - 97) + (k[(j % cipherlen)]) - 97) % 26 + 97);
j++;
}
else
printf ("%c", p[i]);
}
The inner if/else if chain handles this case correctly.
The current plaintext character p[i] and the current keyword character k[j % cipherlen] can be uppercase / lowercase independently. Your code currently does not handle this at all; instead it assumes that if p[i] is uppercase, k[j % cipherlen] must be uppercase as well, and similarly for lowercase.
By the way, I recommend against writing 65 and 97 in the code. I'd use 'A' and 'a' instead, respectively, which makes things more readable IMHO.
To fix this issue, you have to test k[j % cipherlen] for uppercase / lowercase separately. For example:
for (int i = 0, j = 0, n = strlen(p); i < n; i++)
{
char key_char = k[j % cipherlen];
int key_shift;
if (isupper(key_char)) {
key_shift = key_char - 'A';
} else {
key_shift = key_char - 'a';
}
if (isupper(p[i]))
{
printf("%c", ((p[i] - 'A') + key_shift) % 26 + 'A');
j++;
}
else if (islower(p[i]))
{
printf("%c", ((p[i] - 'a') + key_shift) % 26 + 'a');
j++;
}
else
printf ("%c", p[i]);
}
(I got tired of repeatedly typing the same expressions, so I extracted the common bits out to variables (key_char, key_shift). The only tricky part here is that j should only be incremented if key_shift is actually used, but your code already handles that.)
This is a subtle point, but all the <ctype.h> functions (such as isupper, isalpha, ...) have undefined behavior if the argument is negative. char is a signed type in many implementations, so a random character str[i] may well be negative. To be completely portable and correct, you should cast the character to (unsigned char) in each such call:
if (isupper((unsigned char)key_char))
...
if (isupper((unsigned char)p[i]))
...
else if (islower((unsigned char)p[i]))
...
Alternatively, just fully embrace ASCII (the rest of your code assumes it already) and do:
if (key_char >= 'A' && key_char <= 'Z')
...
if (p[i] >= 'A' && p[i] <= 'Z')
...
else if (p[i] >= 'a' && p[i] <= 'z')
...
Related
I am doing Caesar exercise from CS50 course, but it fail.
Here is the code:
# include <stdio.h>
# include <cs50.h>
# include <string.h>
# include <stdlib.h>
# include <ctype.h>
int main(int argc, string argv[])
{
if (argc == 2)
{
string key = argv[1];
int l = strlen(key);
for (int i = 0; i < l; i++)
{
// if key[i] is an alphabet character
if (isalpha(key[i]) != 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
}
//change charater to number
int k = atoi(argv[1]);
//print Plaintext
string plaintext = get_string("Plaintext: ");
int n = strlen(plaintext);
char ciphertext[n];
//declare plaintext in ASCII
int nplaintext[n];
//Change plaintext to ASCII
for (int i = 0; i < n; i++)
{
nplaintext[i] = (int)plaintext[i];
}
//Declare ASCII for ciphertext which we name "plusplaintext"
int plusnplaintext[n];
for (int i = 0; i < n; i++)
{
//if Capital
if ((nplaintext[i] < 91) && (nplaintext[i] > 64))
{
plusnplaintext[i] = 65 + ((nplaintext[i] + k) - 65) % 26 ;
}
//if Lowercase
else if ((nplaintext[i] < 123) && (nplaintext[i] > 96))
{
plusnplaintext[i] = 97 + ((nplaintext[i] + k) - 97) % 26 ;
}
//if not character a -> z and A -> Z
else
{
plusnplaintext[i] = nplaintext[i];
}
}
for (int i = 0; i < n; i++)
{
ciphertext[i] = (char)plusnplaintext[i];
}
printf("ciphertext: %s\n", ciphertext);
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
Here is the output from Check50:
:) caesar.c exists.
:) caesar.c compiles.
**:( encrypts "a" as "b" using 1 as key, output not valid ASCII text**
:) 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, output not valid ASCII text**
:) handles lack of argv[1]
When I test the code, sometimes it gives right result, sometimes it give the wrong result with some more characters at the end...
How can I correct my code?
The problem is with the logic here
//if Capital
plusnplaintext[i] = 65 + ((nplaintext[i] + k) - 65) % 26 ;
and
//if Lowercase
plusnplaintext[i] = 97 + ((nplaintext[i] + k) - 97) % 26 ;
use this logic instead
//if Capital
((((nplaintext[i] - 90) + 25) + key) % 26) + 65
//if Lowercase
((((nplaintext[i] - 122) + 25) + key) % 26) + 97
and it is also not necessary to assign the letters to a string, instead you can directly print it
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
// Error Checking on the command line argument
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
else if (argc == 2)
{
for (int i = 0; i < strlen(argv[1]); i++)
{
if (isdigit(argv[1][i]) == 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
}
}
int key = atoi(argv[1]);
string plain_text = get_string("plaintext: ");
printf("ciphertext: ");
for (int i = 0; i < strlen(plain_text); i++)
{
if (isalpha(plain_text[i]) != 0)
{
// printf("%c", plain_text[i] + key);
if (isupper(plain_text[i]))
{
printf("%c", ((((plain_text[i] - 90) + 25) + key) % 26) + 65);
}
else
{
printf("%c", ((((plain_text[i] - 122) + 25) + key) % 26) + 97);
}
}
else
{
printf("%c", plain_text[i]);
}
}
printf("\n");
}
Instead of using the ascii value of a letter in your code, it could be helpful to keep them as characters for the sake of clarity. I took a snippet of your code and changed the values to demonstrate.
//if Capital
if ((nplaintext[i] <= 'Z') && (nplaintext[i] >= 'A'))
{
plusnplaintext[i] = 'A' + ((nplaintext[i] + k) - 'A') % 26 ;
}
This makes it clear what those values represent, and you don't have to reference an ascii chart. More comments would also help a user, or yourself later on, understand what the purpose is for each part of your code.
You could also create some functions outside of your main function. For example, to check for all digits in command line input, or change the letters by the amount given in the key. If you start working on long programs of code, it could be helpful to have functions defined that you can use as many times as you need. It also cleans up your main for clarity.
This function below takes the character of each letter in the original text (char p), then "moves" the character by k spaces (int k, given by the user as the key in the command line). It works when called in a for loop in main that iterates over each letter in the original given string (text[i]), with i being increased for each time the loop executes.
char rotate(char p, int k)
{
// declare variable for the rotated letter to be stored
char c;
// check if it is a letter
if (isalpha(p))
{
// check for lowercase letter
if (islower(p))
{
// subtract ascii value from p to initialize to 0 - 25 for computations
p -= 'a'; // if p is a, it is now initialized to 0, if b to 1, if c to 3, etc
c = (p + k) % 26; // use Caesar's algorithm to rotate letters, 'wrapping' by using % 26
c += 'a'; // add ascii value back to result to get the rotated letter
}
// the only other option is uppercase since we checked for only letters, do the same for uppercase letters as for lowercase
else
{
p -= 'A';
c = (p + k) % 26;
c += 'A';
}
}
// if it is the nul character '\0' return 0, do not print
else if (p == '\0')
{
return 0;
}
// if not a letter or nul character, return the character as is
else
{
c = p;
}
// print the rotated letter which is now stored in c
printf("%c", c);
// return the value of c
return c;
}
I am doing a problem set from the CS50 course and we have to implement Caesar's cipher. The following code works only with numbers (they remain the same as intended), when you put in a character, however, nothing is output. What's wrong?
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
//problem set requires arguments
int main (int argc, string argv [])
{
int i = 0;
if (argc != 2){
printf ("Retry\n");
return 1;
} else {
int x = atoi(argv [1]);
//gets plaintext
string a = get_string ("plaintext:");
printf("ciphertext:");
for (i = 0; i <= strlen(a); i++){
//if character is a number it remains unchanged
if (isdigit(a[i])){
printf ("%c", a[i]);
} else {
if (isupper(a[i])){
//converts from ASCII index to alphabetical index
char y = a[i] - 65;
//Caesar cipher formula. If upper case remains upper case.
y = toupper(a[i] + x % 26);
//goes back ASCII
printf("%c", y + 65);
} else if (islower(a[i])){
//converts from ASCII index to alphabetical index
char t = a[i] - 65;
//Caesar cipher formula. If lower case remains lower case.
t = tolower(a[i] + x % 26);
//goes back to ASCII
printf("%c", t + 65);
}
}
}
}
}
65 is the ASCII value for letter 'A'. If you are working on lower case word, you would need 97, which is ASCII value for 'a'
You have calculated t, but you didn't carry the result to the next line. Moreover, you have to take the modulus on the whole line. You are expecting that t is between 0 and 26, then you add it to 97
char t = a[i] - 97;
t = (t + x) % 26;
printf("%c", t + 97);
Also the for loop should go up to strlen(a). The last index in the string a is null-character, it should not be modified. You can also use 'a' instead of 97 here.
for(i = 0; i < strlen(a); i++)
{
if(isupper(a[i]))
{
char y = a[i] - 'A';
y = (y + x ) % 26;
printf("%c", y + 'A');
}
else if(islower(a[i]))
{
char t = a[i] - 'a';
t = (t + x )% 26;
printf("%c", t + 'a');
}
else
{
printf("%c", a[i]);
}
}
First of all there is no one that I can ask this kind of question so please pardon me
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[]) {
string key = argv[1];
int l = strlen(argv[1]);
if (argc != 2) {
return 0;
}
for (int i = 0, n = strlen(key); i < n; i++) {
if (!isalpha(key[i])) {
return 0;
}
key[i] = tolower(key[i]);
key[i] = key[i] - 97;
}
string txt = GetString();
for (int k = 0, p = strlen(txt); k < p; k++) {
if (isalpha(txt[k])) {
if (isupper(txt[k])) {
printf("%c", (((txt[k] - 65) + (key[k % l])) % 26 + 65));
}
if (islower(txt[k])) {
printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97));
}
} else
if (!isalpha(txt[k])) {
printf("%c", txt[k]);
}
}
printf("\n");
return 0;
}
I can't quite get these 2 lines of code
key[i] = key[i] - 97;
printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97));
Is there an easy explanation of why did we use the first and how the second one works?
The key used for the Vigenere cypher is supposed to be all letters. The first expression converts the string into an array of offsets, 0 for a, 1 for b, etc. 97 is the ASCII code for 'a'. It would be more readable to write:
for (int i = 0, n = strlen(key); i < n; i++) {
if (!isalpha((unsigned char)key[i])) {
printf("key '%s' must contain only letters\n", key);
return 1;
}
key[i] = tolower((unsigned char)key[i]);
key[i] = key[i] - 'a';
}
For the second expression, if the character txt[k] is a lower case letter, printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97)); computes and prints the transposed letter by adding the shift value (each character in key is used as a shift value one after the other, shifting by 0 for a, 1 for b etc.). Here are the steps:
The program computes the letter index txt[k] - 97, 97 being the ASCII code for 'a',
it then adds the shift value key[k % l], cycling the values in key in a circular fashion,
it takes the modulo 26 to get a letter index between 0 and 25.
it finally adds 97, the ASCII value of 'a' to convert the index back into a lowercase letter.
It would be less redundant and more readable to write it this way:
for (int i = 0, j = 0; txt[i] != '\0'; i++) {
int c = (unsigned char)txt[i];
if (isupper(c)) {
c = (c - 'A' + key[j++ % l]) % 26 + 'A';
} else
if (islower(c)) {
c = (c - 'a' + key[j++ % l]) % 26 + 'a';
}
putchar(c);
}
Also note that argv[1] should not be passed to strlen() before checking that enough arguments have been passed on the command line.
Here is a modified version of the program:
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, string argv[]) {
if (argc != 2) {
printf("missing key argument\n");
return 1;
}
string key = argv[1];
int klen = strlen(key);
if (klen == 0) {
printf("key cannot be empty\n");
return 1;
}
for (int i = 0; i < klen; i++) {
if (!isalpha((unsigned char)key[i])) {
printf("key '%s' must contain only letters\n", key);
return 1;
}
key[i] = tolower((unsigned char)key[i]) - 'a';
}
string txt = GetString();
for (int i = 0, j = 0; txt[i] != '\0'; i++) {
int c = (unsigned char)txt[i];
if (isupper(c)) {
c = (c - 'A' + key[j++ % klen]) % 26 + 'A';
} else
if (islower(c)) {
c = (c - 'a' + key[j++ % klen]) % 26 + 'a';
}
putchar(c);
}
putchar('\n');
return 0;
}
key[i] = key[i] - 97;
That line's use is to give key[i], which value represents the value of a caracter in ascii it's index in our alphabet. Then, 'a' will be given the value 0, 'b' the value 1 .... , and 'z' the value 25.
As for the second line,
printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97))
it prints the caracter which's ascii value is
(((txt[k] - 97) + (key[k % l])) % 26 + 97))
The substraction of 97 has the same purpose as explained above.
The % 26 is the modulus, ie the remainder of ((txt[k] - 97) + (key[k % l])) when divided per 26 (integer division). Then, 97 is added again to convert the order, or index of the result into a corresponding ascii value.
This page might give you some more insight about the character representation in C.
As for the meaning of k,i and l, I let you grasp the inner functionning of the cypher by yourself, but the whole encryption happens in the second line you wished an explanation for.
PS : The parts with '65' are just the same, but with uppercase letters, since 'A' value in ascii is 65.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
Pseudocode explanation of what I am attempting to do.
Convert a character array into an integer array
Iterate through each integer in that array and add q to it, unless that integer + q exceeds an upper bound. If it exceeds that number, return the modulus and add the modulus to a lower bound.
print the converted integer array in its ASCII sequence using %c.
Here is the example:
int main(void)
{
char char_message[] = "abcyzABCYZ";
int q = 10;
int i;
int message[10];
int n = strlen(char_message);
for(i = 0; i < n; i++) {
message[i] = atoi(&char_message[i]);
}
for(i = 0; i < n; i++) {
if(message[i] <= 90) {
if (message[i] + q <= 90) {
message[i] = message[i] + q;
}
else
message[i] = 65 + (message[i] % 90);
}
else if(message[i] >= 97) {
if (message[i] + q <= 122) {
message[i] = message[i] + q;
}
else
message[i] = 97 + (message[i] % 122);
}
}
for(i = 0; i < n; i++) {
printf("%c", message[i]);
}
return 0;
}
EDIT: Below is a second attempt at this problem --------------------------
int main(int argc, char *argv[]) {
if(argc != 3) {
printf("Enter an integer followed by a string \n\n");
return 1;
}
int i;
int offset = atoi(argv[1]);
char **p_message;
p_message = &argv[2];
char encrypt[strlen(*p_message)];
printf("You Entered: %d, %s \n", offset, *p_message);
for(i = 0; i < strlen(*p_message); i++)
{
encrypt[i] = ((*p_message[i] + offset) % 26);
}
for(i = 0; i < strlen(*p_message); i++)
{
printf("%c", encrypt[i]);
}
return 0;
}
I'm not doing your homework for you, but it is obvious that two things are clearly wrong.
Your use of atoi is incorrect. It should not be here at all.
Your calculation of the resulting enciphered character is wrong as well. The modulus placement is wrong in two different locations.
Your use of "magic numbers" in this code is rampant making it much, much more difficult to read than need-be. Avoid using magic numbers.
The following "enciphers" your test string via a simple forward scanning loop, output each resulting character one at a time. I've left the storage to a separate int array for you to handle. Of note the first if-block is expanded out statement by statement so you can see what is going on one step at a time. The second (lower case handling) is done in a single expression. Other than different ranges, the two methods of calculation are equivalent.
Note: this only works on platforms where character ranges A..Z and a..z are continuous. The language standard makes no enforcement of this; it only enforces it for digit characters 0..9. Thus, don't blame me if you run this on an AS/400 or OS/390 (both EBCDIC platforms) and it doesn't work.
#include <stdio.h>
#include <string.h>
int main()
{
char message[] = "abcyzABCYZ";
const int q = 10;
puts(message);
for(const char *p = message; *p; ++p)
{
int c = (unsigned char)*p;
if (c >= 'A' && c <= 'Z')
{
c -= 'A';
c += q;
c %= ('Z' - 'A' + 1);
c += 'A';
}
else if (c >= 'a' && c <= 'z')
c = ((c - ('a' - q)) % ('z' - 'a' + 1)) + 'a';
// else nothing. keep as-is
fputc(c, stdout);
}
fputc('\n', stdout);
return 0;
}
Output
abcyzABCYZ
klmijKLMIJ
This is a Vigenere cypher for cs50. It's my first time coding and I'm going around this for a week now, and I don't seem to be able to print first letter after the loop finishes for the first time.
For example:
jharvard#appliance (~/Dropbox): ./viginere abcde
You're key is abcde
Type your text:
aaaaa aaaaaa aaaaaaa aaaaaaaa
abcde bcde bcdebc debcde
First a is printed but then it starts on b and in the end it doesn't print every letter. The key is chosen by the user.
I have no idea what I'm doing wrong.
for (int i = 0, j = strlen(plain_text), l = 0; i < j; i++)
{
int rotation_1 = (tolower(plain_text[i]) + (key[l] - 97)) % 122;
int rotation_2 = (plain_text[i] + (key[l] - 97)) % 122;
//if it is a letter
if (isalpha(plain_text[i]))
{
l = l % strlen(key);
//if the it is uppercase
if (isupper(plain_text[i]))
{
printf("%c", toupper(rotation_1));
}
//else if it is lowercase
else
{
printf("%c", rotation_2);
}
l++;
}
// if it is not a letter we print it as it is
else
{
printf("%c", plain_text[i]);
}
}
Here is a simpler version with the logic fixed:
for (int i = 0, n = strlen(plain_text), k = 0, klen = strlen(key); i < n; i++) {
int c = (unsigned char)plain_text[i];
//if it is a letter
if (isalpha(c)) {
if (isupper(c)) {
c = 'A' + (c - 'A' + key[k] - 'a') % 26;
} else {
c = 'a' + (c - 'a' + key[k] - 'a') % 26;
}
k = (k + 1) % klen;
}
putchar(c);
}
It is important to cast plain_text[i] as (unsigned char) because isalpha and isupper are only defined for all values of unsigned char and EOF. char may be signed by default of your platform.
Also note that putchar(c) is much more efficient than printf("%c", c);