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);
}
}
}
Related
I'm working on a program that is supposed to take a key as an input argument, and encrypt a user input word using this key.
The program should:
Ask the user for a plaintext word to encrypt
Standardize the letter case
Take each letter from the plaintext and find the index of this letter (A = 0, B = 1,...)
Look at the letter indexed at this location in the key string (input argument)
Assign this encrypted letter to a new sting called cypher
Print the new cyphertext string.
The code I'm using is this:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
//Check that key has 26 letters or end program
string key = argv[1];
if (strlen(argv[1]) != 26)
{
printf("Key must contain 26 characters\n");
return 1;
}
//Get plaintext
string plain = get_string("plaintext: ");
//Make key all letters upper case
for (int i = 0; i < plain[i]; i++)
{
if (islower(plain[i]))
{
plain[i] = plain[i] - 32;
}
printf("%c", plain[i]);
}
printf("\n");
//Encrypt
int index[] = {};
int cypher[] = {};
//Cycle through the letters in the word to be encoded
//printf("cyphertext: ");
printf("%c\n", key[79 - 65]);
for (int i = 0; i < strlen(plain); i++)
{
printf("index in key: %i\n", plain[i] - 65);
cypher[i] = key[plain[i] - 65];
printf("cypher: %c\n", cypher[i]);
}
printf("\n");
}
Everything executes fine until the fourth loop of the for loop that assigns the new values to the cypher string. When the program tries to set i = 4, I get the error Segmentation fault (core dumped)
I was expecting the last for loop to loop once for each letter of the input (e.g. input: hello; loops: 5), but I found that it stops at 4 and only outputs: 'HELL'.
I tried:
Words with 4 characters - executes the correct number of loops, but I still get Segmentation fault (core dumped) after the final loop
Words with 3 characters - executes fine, no error
Words with 5+ letters - Still loops 4 times before error
Please help!
The for loop should iterate from 0 to length of plain.
//Get plaintext
string plain = get_string("plaintext: ");
//Make key all letters upper case
for (int i = 0; i < strlen(plain); i++)
{
if (islower(plain[i]))
{
plain[i] = plain[i] - 32;
}
printf("%c", plain[i]);
}
//*** Must allocate memory for array
//Encrypt
int index[100] = {};
int cypher[100] = {};
//Cycle through the letters in the word to be encoded
//printf("cyphertext: ");
printf("%c\n", key[79 - 65]);
for (int i = 0; i < strlen(plain); i++)
{
printf("index in key: %i\n", plain[i] - 65);
cypher[i] = key[plain[i] - 65];
printf("cypher: %c\n", cypher[i]);
}
printf("\n");
I've implemented the Caesar's cipher in C, and, despite the algorithm is working, I didn't understood why (sometimes) I get an empty value if I do not subtract the first letter of the alphabet before adding the key. Here's the full code (see line 59 or search for return (letter + k) % 26):
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
string caesar_cipher(string text, int k);
char replace_letter(char letter, int k);
bool is_numeric(string input);
int main(int argc, string argv[])
{
if (argc != 2 || (argc == 2 && !is_numeric(argv[1])))
{
fprintf(stderr, "You must specify a key to the cipher! Exiting...\n");
exit(EXIT_FAILURE);
}
// Convert command line argument to integer.
int k = atoi(argv[1]);
// Prompts user for the text to encrypt
string text = get_string("plaintext: ");
// Returns encrypted text
printf("ciphertext: %s\n", caesar_cipher(text, k));
exit(EXIT_SUCCESS);
}
string caesar_cipher(string text, int k)
{
int text_length = strlen(text);
string ciphered_text = text;
for (int i = 0; text[i] != '\0'; i++)
{
ciphered_text[i] = replace_letter(text[i], k);
}
return ciphered_text;
}
char replace_letter(char letter, int k)
{
// Early return when 'letter' is a non-alphabetical character
if (!isalpha(letter))
{
return letter;
}
char operation_letter = 'a';
if (isupper(letter))
{
operation_letter = 'A';
}
// return (letter + k) % 26; // Sometimes, returns an empty value
return ((letter - operation_letter + k) % 26) + operation_letter;
}
// Loop over characters to check if each one of them is numeric
bool is_numeric(string input)
{
for (int i = 0; input[i] != '\0'; i++)
{
// If character is not numeric
// returns false.
if (isdigit(input[i]) == 0)
{
return false;
}
}
return true;
}
Can anybody explain why this happens?
You need to account for the first letter of the alphabet (either a or A) in your functions because chars are internally represented as an integer number (usually only a single byte, but it depends on the encoding). In ASCII for example, doing a % 26 will result in any of the 26 first values of the ASCII table, none of which are actual letters. Hopefully I made myself clear.
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.
I have spent hours on this and I am still stuck. I am getting the following output when I do my check. Do the errors have something to do with the way I am printing it out?
:) vigenere.c exists
:) vigenere.c compiles
:( encrypts "a" as "a" using "a" as keyword
\ expected output, but not "ciphertext: a\u0004ù\u001bÿ\n"
:) encrypts "barfoo" as "caqgon" using "baz" as keyword
:) encrypts "BaRFoo" as "CaQGon" using "BaZ" as keyword
:) encrypts "BARFOO" as "CAQGON" using "BAZ" as keyword
:( encrypts "world!$?" as "xoqmd!$?" using "baz" as keyword
\ expected output, but not "ciphertext: xoqmd!$?í\b#\n"
:( encrypts "world, say hello!" as "xoqmd, rby gflkp!" using "baz" as keyword
\ expected output, but not "ciphertext: xoqmd, rby gflkp!^¿µÿ\n"
:) handles lack of argv[1]
:) handles argc > 2
:) rejects "Hax0r2" as keyword
Here is the code:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#define alpha_length 26
char secret(char character, int key);
int main(int argc, string argv[]) {
//check that there are only two strings
if (argc != 2) {
printf("Usage: ./vignere k\n");
return 1;
}
//check that argv1 is alphabetical
string code = argv[1];
for (int t = 0; t < strlen(code); t++) {
if (!isalpha(code[t])) {
printf("Alphabetical only!\n");
return 1;
}
}
//get string from user to encrypt
printf("plaintext: ");
string plaintext = get_string();
//array created out of user inputted plain text
char cypher[strlen(plaintext)];
//j counts the number of alphabetical characters so that it resets based on argv length
int j = 0;
//iterate over characters in array. If they are alpha then apply the function secret
for (int i = 0; i < strlen(plaintext); i++) {
if (isalpha(plaintext[i])) {
int index = j % strlen(code);
int code_index = toupper(code[index]) - 'A' ;
cypher[i] = secret(plaintext[i], code_index);
j = j + 1;
} else {
cypher[i] = plaintext[i];
}
}
printf("ciphertext: %s\n", cypher);
return 0;
}
char secret (char character, int key) {
char shift;
// if the character is upper case then start with uppercase A and shift based on the appropriate character from argv1
if (isupper(character)) {
shift = (int)character -'A';
shift = shift + key;
shift = (shift % alpha_length) + 'A';
} else {
// else start wit lower case a
shift = (int)character - 'a';
shift = shift + key;
shift = (shift % alpha_length) + 'a';
}
return (char)shift;
}
There are multiple problems in your code:
do not use the string typedef from <cs50.h>: it hides the nature of the object you are manipulating, a simple char * pointer which you should learn to master without fear.
because type char can be signed by default and have a negative value for which isalpha() is undefined, you should cast the char arguments to these functions as unsigned char: isalpha((unsigned char)code[t])
you intend for cypher to be a C string, so you must allocate an extra byte for the null terminator and store it there:
char cypher[strlen(plaintext) + 1];
cipher is spelled with an i.
Here is a modified version:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#define alpha_length 26
char secret(char character, int key);
int main(int argc, char *argv[]) {
//check that there are only two strings
if (argc != 2) {
printf("Usage: ./vignere k\n");
return 1;
}
//check that argv1 is alphabetical
char *code = argv[1];
int code_len = strlen(code);
for (int t = 0; t < code_len; t++) {
if (!isalpha((unsigned char)code[t])) {
printf("Alphabetical only!\n");
return 1;
}
}
//get string from user to encrypt
printf("plaintext: ");
char *plaintext = get_string();
int text_len = strlen(plaintext);
//array created out of user inputted plain text
char cipher[text_len + 1];
//j counts the number of alphabetical characters so that it resets based on argv length
int j = 0;
//iterate over characters in array. If they are alpha then apply the function secret
for (int i = 0; i < text_len; i++) {
if (isalpha((unsigned char)plaintext[i])) {
int index = j % code_len;
int code_index = toupper((unsigned char)code[index]) - 'A';
cipher[i] = secret(plaintext[i], code_index);
j = j + 1;
} else {
cipher[i] = plaintext[i];
}
}
cipher[text_len] = '\0';
printf("ciphertext: %s\n", cipher);
return 0;
}
char secret (char character, int key) {
int shift;
// if the character is upper case then start with uppercase A and shift based on the appropriate character from argv1
if (isupper((unsigned char)character)) {
shift = (int)character - 'A';
shift = shift + key;
return 'A' + (shift % alpha_length);
} else {
// else start with lower case a
shift = (int)character - 'a';
shift = shift + key;
return 'a' + (shift % alpha_length);
}
}
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, string argv[])
{
string k = argv[1];
string s = GetString();
int l = strlen(k);
for(int i = 0, n = strlen(s); i < n; i++)
{
if(s[i] >= 65 && s[i] <= 90)
{
int i2 = ((s[i]-65) + (k[i%l]-97)) % 26;
printf("%c", i2+65);
} else if(s[i] >= 97 && s[i] <= 122)
{
int i2 = ((s[i]-97) + (k[i%l]-97)) % 26;
printf("%c", i2+97);
} else
{
printf("%c", s[i]);
}
}
printf("\n");
return 0;
}
I have removed as many parts as I can in order to make the code more relevant to the question. Basically why does this code work when "s" does not have any space(" ") in it and doesn't when "s" consists of space(" ")?
As most of you may know the idea is the argument entered at argv[1] is the "keyword" for the cipher. User then inputs a "plain-text" to cipher (s). It works when I try with various words or sentences if it doesn't include any space, " ". I just don't understand the logic behind this. Why does the cycle break if s[i] is not one of the first two conditions - I would have thought that "else" condition would work.
I would really appreciate it if someone can shed some light on this - many thanks in advance!
ps: I know there are some extra libraries at the top and the user input at argv[1] is not verified via isalpha(). I just want to understand the cycle process better for now, I have those checks in another file ready.
Here is code that implements the 'separate counters for string and key' comment that I made. It also uses the letter codes 'a' and 'A' (and avoids needing to use 'z' or 'Z') instead of using numbers. It does assume that you are dealing with a single-byte code set (not UTF-8 unless you're working in the ASCII range) where the lower-case and upper-case letters are each in a contiguous range (so it won't work reliably with EBCDIC, but will with most other code sets), and it also ignores accented characters. (It would have to do setlocale("") to get locale-specific interpretations of which characters are letters.)
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s key\n", argv[0]);
return 1;
}
string k = argv[1];
int l = strlen(k);
for (int i = 0; i < l; i++)
{
int c = k[i];
if (!isalpha(c))
{
fprintf(stderr, "%s: non-alpha character %c in key string\n", argv[0], c);
return 1;
}
k[i] = tolower(c);
}
printf("Enter a string to be encrypted:\n");
string s = GetString();
int n = strlen(s);
for (int i = 0, j = 0; i < n; i++)
{
int c = (unsigned char)s[i];
if (isupper(c))
c = ((c - 'A') + (k[j++ % l] - 'a')) % 26 + 'A';
else if (islower(c))
c = ((c - 'a') + (k[j++ % l] - 'a')) % 26 + 'a';
putchar(c);
}
putchar('\n');
return 0;
}
Here is a sample run that demonstrates the weakness of using 'a' as one of the letters in the key for this Vigenere cipher:
./vc caesArandAbrAcaDabRa
Enter a string to be encrypted:
It is reported that Caesar said "Veni, vidi, vici" when he conquered Britain.
Kt mk rvpbutfu tjaw Cbvscr wsiu "Vrqi, wzdk, vlcj" nhgn lw cfndxesvd Drltbzn.