I have created the following code as an answer to CS50x PSET2: Vigenere and it works to some extent however when running through check50 I get some errors listed below:
:) vigenere.c exists.
:) vigenere.c compiles.
:) encrypts "a" as "a" using "a" as keyword
:( encrypts "barfoo" as "caqgon" using "baz" as keyword - output not valid ASCII text
:( encrypts "BaRFoo" as "CaQGon" using "BaZ" as keyword - output not valid ASCII text
:) encrypts "BARFOO" as "CAQGON" using "BAZ" as keyword
:( encrypts "world!$?" as "xoqmd!$?" using "baz" as keyword- output not valid ASCII text
:( encrypts "hello, world!" as "iekmo, vprke!" using "baz" as keyword- output not valid ASCII text
:) handles lack of argv[1]
:) handles argc > 2
:( rejects "Hax0r2" as keyword - timed out while waiting for program to exit
What seems to be happening is where the key contains a high value (i.e z/Z) it causes the code to skip to the next line and miss out what appears to be random sequences. eg. in the first word of the string it missed out the 3rd character, then the second word it misses the 3rd and 4th and then the third word the 1st. I just can't understand what is happening.
I have used printf to ensure that all the variables being set and passed into functions are correct at runtime. The functions themselves are returning the correct responses (except validation of Hax0r2). I have tried debugging by comparing results to an online vigenere cipher tool.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int Validate1(int argc);
int Validate2(string argv);
void Cypher(string x);
void KeyCalc(string argv);
string MESSAGE;
int LENGTH;
int *KEY;
int COUNTER = 0;
int main(int argc, string argv[])
{
//Check if right amount of arguments are supplied
int Val1 = Validate1(argc);
if (Val1 == 0)
{
//Check if argument is a string of chars
int Val2 = Validate2(argv[1]);
if (Val2 == 0)
{
//get the string length
LENGTH = strlen(argv[1]);
//Dynamically update KEY array length
KEY = (int *)malloc(LENGTH * sizeof(*KEY));
if (KEY == NULL)
{
fprintf(stderr, "malloc failed\n");
}
//calculate the key
KeyCalc(argv[1]);
//get the message from the user to be encrypted
MESSAGE = get_string("plaintext: ");
printf("ciphertext: ");
//encrypt message from user
Cypher(argv[1]);
free(KEY);
return 0;
}
else
{
//validation failed
printf("Usage: ./vigenere keyword\n");
return 1;
}
}
else
{
//validation failed
printf("Usage: ./vigenere keyword\n");
return 1;
}
}
//Validate the number of arguments supplied
int Validate1(int argc)
{
if (argc != 2)
{
return 1;
}
else
{
return 0;
}
}
//Validate the argument is a string
int Validate2(string argv)
{
int k = 0;
//loop through all characters in argument line string and check if alphabetic
for (int i = 0; i < LENGTH; i++)
{
if isalpha(argv[i])
{
//Do Nothing
}
else
{
k++;
}
}
//k counts the number of non-alphabetic characters, so if > 0 then invalid input
if (k > 0)
{
return 1;
}
else
{
return 0;
}
}
void Cypher(string x)
{
//identify the length of the message to be coded
int Mlength = strlen(MESSAGE);
//identify the length of the key
int Slen = strlen(x);
//cycle through all characters in message supplied by user
for (int i = 0; i < Mlength; i++)
{
// loop through key
if (COUNTER > Slen - 1)
{
COUNTER = 0;
}
//check if the character is alphabetic
if (isalpha(MESSAGE[i]))
{
//convert the character to ASCII int value
char l = MESSAGE[i];
//add key value to message value and wrap around ascii mapping
if (isupper(MESSAGE[i]))
{
l = l + KEY[COUNTER];
if (l > 'Z')
{
l = l - 26;
}
}
else
{
l = l + KEY[COUNTER];
if (l > 'z')
{
l = l - 26;
}
}
//convert value back into character and store in array
MESSAGE[i] = (char) l;
// print character
printf("%c", MESSAGE[i]);
COUNTER++;
}
else
{
//character is 'numeric' or 'symbol' or 'space' just display it
printf("%c", MESSAGE[i]);
}
}
printf("\n");
}
void KeyCalc(string argv)
{
//convert key entry to values A/a = 0 to Z/z = 26
for (int i = 0; i < LENGTH; i++)
{
char k = argv[i];
if (islower(argv[i]))
{
KEY[i] = k - 'a';
}
else
{
KEY[i] = k - 'A';
}
}
}
encrypts "barfoo" as "caqgon" using "baz" as keyword
encrypts "BaRFoo" as "CaQGon" using "BaZ" as keyword
encrypts "world!$?" as "xoqmd!$?" using "baz" as keyword
encrypts "hello, world!" as "iekmo, vprke!" using "baz" as keyword
rejects "Hax0r2" as keyword
From the spec for the caesar pset:
...Caesar’s algorithm (i.e., cipher) encrypts messages by
“rotating” each letter by k positions. More formally, if p is some
plaintext (i.e., an unencrypted message), pi is the ith character in
p, and k is a secret key (i.e., a non-negative integer), then each
letter, ci, in the ciphertext, c, is computed as
ci = (pi + k) % 26
This algorithm (in either "case") does not do that:
l = l + KEY[COUNTER];
if (l > 'Z')
{
l = l - 26;
}
This walkthrough starting at 9:30 is a good primer on how to implement the "shift".
The proximate cause of the problem in this code is that this l = l + KEY[COUNTER]; can yield a result outside the ascii range. In the CS50 implementation, char defaults to a signed char. So, for example, 'r' + 'z' (as in "barfoo" ciphered with "baz") will yield -117.
Related
My program is producing what is seems like the correct output but i still get the :( message when i run check50. I have already read other awnsers to similar questions but none of them seems actually similar to my problem.
check50 output:
:) substitution.c exists
:) substitution.c compiles
:) encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
:) encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
:) encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
:) encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
:) encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
->:( encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
output not valid ASCII text
->:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
output not valid ASCII text
:) encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
:) does not encrypt non-alphabetical characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
:) handles lack of key
:) handles too many arguments
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key
I put the -> before the error messages for easier visualization
It is weird because right before the two errors there is an almost identical input/output that has been checked as correct
Here is my code:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
string subs(string plain, string key);
int key_check(string key);
int main(int argc, string argv[]) {
// CHECK IF IT HAS AN INPUT
if (argc < 2) {
printf("Usage: ./substitution key\n");
return 1;
}
// CHECK IF IT HAS MORE THAN 1 INPUT
if (argc > 2) {
printf("Usage: ./substitution key\n");
return 1;
}
// IF KEYCHECK FUNCTION DETECTS AN ERROR, RETURN 1
if (key_check(argv[1]) == 1) {
return 1;
}
// ELSE KEY = USER ARGV INPUT
string key = argv[1];
// GET USER PLAINTEXT INPUT
string plain = get_string("plaintext: ");
string cipher = subs(plain, key);
// PRINT RESULT
printf("ciphertext: %s\n", cipher);
}
int key_check(string key) {
// STRING LENGHT
int leng = strlen(key);
// CHECK IF KEY HAVE 26 CHARACTERS
if (leng < 26) {
printf("Key must contain 26 characters.\n");
return 1;
}
for (int i = 0; i < leng; i++) {
// CHECK IF KEY ONLY HAVE ALPHABET CHARACTERS
if (isalpha(key[i]) == 0) {
printf("Key must contain only alphabet characters\n");
return 1;
}
// CHECK IF KEY HAVE REPEATED CHARACTER
for (int i2 = 0; i2 < 26; i2++) {
if (i != i2) {
if (key[i] == key[i2]) {
printf("Key must have each character exactly one time\n");
return 1;
}
}
}
}
return 0;
}
string subs(string plain, string key) {
// GET PLAINTEXT LENGHT
int leng = strlen(plain);
// CREATES CIPHER STRING
string cipher = plain;
// CREATES AN ARRAY FOR UPPER KEY
int UPPER[26];
for (int i2 = 0; i2 < 26; i2++) {
if (isupper(key[i2]) > 0 ) {
UPPER[i2] = key[i2];
}
else {
UPPER[i2] = key[i2] - 32;
}
}
// CREATES AN ARRAY FOR LOWER KEY
int LOWER[26];
for (int i3 = 0; i3 < 26; i3++) {
if (islower(key[i3] > 0)) {
LOWER[i3] = key[i3];
}
else {
LOWER[i3] = key[i3] + 32;
}
}
for (int i = 0; i < leng; i++) {
if (isupper(plain[i]) > 0) {
cipher[i] = UPPER[plain[i] - 65];
}
else if (islower(plain[i]) > 0) {
cipher[i] = LOWER[plain[i] - 97];
}
else {
cipher[i] = plain[i];
}
}
return cipher;
}
It all leads me to think that it is a check50 problem, but with my lack of experience with coding and problem solving it can be anything.
Thanks in advance.
The line if (islower(key[i3] > 0)) { has parentheses in the wrong place. It should be:
if( islower(key[i3]) > 0 ){
or (more typical in C):
if( islower(key[i3]) ){
To get upper and lower working.
Lowercase needs to point to i2.
for (int i2 = 0; i2 < 26; i2++) {
if (islower(key[i2] > 0)) {
LOWER[i2] = key[i2];
}
else {
LOWER[i2] = key[i2] + 32;
I am having troubles with this submission, I don't see a problem with my code and it works fine when I test it but for some reason I don't know when I use check50, it returns this result:
:) substitution.c exists
:) substitution.c compiles
:( encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
Cause
output not valid ASCII text
:( encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
Cause
output not valid ASCII text
:( encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
Cause
expected "ciphertext: NJ...", not ""
:( encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
Cause
expected "ciphertext: Ke...", not ""
:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
Cause
expected "ciphertext: Cb...", not ""
:) encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
Cause
expected "ciphertext: Cb...", not ""
:( encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
Cause
expected "ciphertext: Rq...", not ""
:) handles lack of key
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key
This results mean that my code outputs the wrong result but when I test the keys and input text myself , it works as it is supposed to, I spent hours trying to figure it out but I am clueless here is my code :
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
bool contains(char c, char arr[], int n)
{
for(int i = 0 ;i < n; i++)
{
if(c == arr[i])
{
return true;
}
}
return false;
}
bool validkey(string key)
{
if (strlen(key) != 26 )
{
return false;
}
char l[26];
for (int i = 0; i < 26; i++)
{
if(isalpha(key[i]))
{
if(contains(key[i],l,i))
{
return false;
}
else
{
l[i] = key[i];
}
}
else
{
return false;
}
}
return true;
}
void cypher(string key, string s)
{
int n = strlen(s);
char r[n+1];
char t;
char a = 'a';
int pos = 0;
char w;
for(int i = 0; i < n; i++)
{
if(isalpha(s[i])){
t = tolower(s[i]);
pos = t - a;
if (islower(s[i]))
{
r[i]= tolower(key[pos]);
}
else if (isupper(s[i]))
{
r[i] = toupper(key[pos]);
}
}
else
{
r[i] = s[i];
}
}
r[n]='\0';
printf("ciphertext: %s\n",r);
}
int main(int argc, string argv[])
{
if(argc != 2)
{
return 1;
}
string key = argv[1];
if(!validkey(key))
{
printf("Invalid");
return 1;
}
string q = get_string("plain text: ");
cypher(key,q);
return 0;
}
Your actual problem looks to be the fact that you have overlooked the simple output required if no-input is provided, or if an invalid key is provided, e.g. from Pset2 - Substitution
And what if a user doesn’t provide a valid key?
$ ./substitution ABC
Key must contain 26 characters.
Or really doesn’t cooperate?
$ ./substitution
Usage: ./substitution key
You fail to provide the correct output in each circumstance.
Now your code is a bit rough as pointed out by #EugeneSh in the comments. Your contains() is a bit awkward and superfluous. What you need instead is a simple frequency-array. An array of 26-integers, initialized all zero, where you map the characters in key from 0-25 (by converting the char tolower() and subtracting 'a' so a-z maps to 0-25. Then for each character in key, you simply check if array[tolower(key[i]) - 'a'] then it is a duplicate char and return false. If not, increment that element and check the next char, e.g.:
#define KEYSZ 26
bool validkey (string key)
{
int keychk[KEYSZ] = {0};
if (strlen (key) != KEYSZ) {
return false;
}
for (int i = 0; key[i]; i++) {
int lowerk = tolower(key[i]) - 'a';
if (!isalpha (key[i]) || keychk[lowerk])
return false;
keychk[lowerk]++;
}
return true;
}
Where the keychk[] array is used for that purpose.
Your cypher() function should really return type string so the cipher text is available to the calling function, not simply output. (that isn't an error, just a practical consideration). You can rewrite and simplify your cypher() function (renamed: encipher()) as:
string encipher (string key, string s, string cipher)
{
int i = 0;
for (; s[i]; i++) {
if (isalpha (s[i])) {
int pos = tolower (s[i]) - 'a';
cipher[i] = islower(s[i]) ? tolower(key[pos]) : toupper(key[pos]);
}
else
cipher[i] = s[i];
}
cipher[i] = 0;
return cipher;
}
Those are really the only two functions needed. If you put it altogether you would have:
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define KEYSZ 26
bool validkey (string key)
{
int keychk[KEYSZ] = {0};
if (strlen (key) != KEYSZ) {
return false;
}
for (int i = 0; key[i]; i++) {
int lowerk = tolower(key[i]) - 'a';
if (!isalpha (key[i]) || keychk[lowerk])
return false;
keychk[lowerk]++;
}
return true;
}
string encipher (string key, string s, string cipher)
{
int i = 0;
for (; s[i]; i++) {
if (isalpha (s[i])) {
int pos = tolower (s[i]) - 'a';
cipher[i] = islower(s[i]) ? tolower(key[pos]) : toupper(key[pos]);
}
else
cipher[i] = s[i];
}
cipher[i] = 0;
return cipher;
}
int main (int argc, string argv[])
{
if (argc < 2) {
fputs ("Usage: ./substitution key\n", stderr);
return 1;
}
size_t len = 0;
string key = argv[1];
if (!validkey (key)) {
fputs ("Key must contain 26 characters.\n", stderr);
return 1;
}
string plain = get_string ("plaintext: "), cipher;
len = strlen (plain);
cipher = malloc (len + 1);
printf ("ciphertext: %s\n", encipher (key, plain, cipher));
free (cipher);
return 0;
}
(note: main() updated to allocate separately for cipher allocating for the number of characters in plain (+1 for the nul-terminating character) and changed output prompts, e.g. "plaintext " and "ciphertext: " as per the problem, and added #include <stdlib.h> which is not included by default with the online ide.cs50.io CS50 IDE)
Give that a try and let me know if you have problems or need additional help.
In CS50, I'm trying to end my substitution exercise and I've an issue but don t know how to solve.
This is my code:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
int get_validkey(string Text);
int get_Alpha_to_code(char charac);
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("usage: ./substitution key\n");
return 1;
}
int validation = get_validkey(argv[1]);
if (validation != 0)
{
if(validation == 1)
{
printf("key must contain 26 alphabetical characters\n");
}
else
{
if (validation == 2)
{
printf("some charaters are not alphabetic\n");
}
else
{
printf("some charaters are repeated\n");
}
}
return 1;
}
else
{
// constants
string code = argv[1];
int charc;
int j;
// ask for message to encrypt
string tocode = get_string("plaintext: ");
// transform to code
int charcount = strlen(tocode);
char codedmessage[charcount];
for (int i = 0; i < strlen(tocode); i++)
{
// check type of character
if ((tocode[i]>='a' && tocode[i]<='z') || (tocode[i]>='A' && tocode[i]<='Z'))
{
j = get_Alpha_to_code(tocode[i]);
if(islower(tocode[i]))
{
codedmessage[i] = tolower(code[j]);
}
else
{
codedmessage[i] = toupper(code[j]);
}
}
else
{
codedmessage[i] = tocode[i];
}
}
codedmessage[strlen(codedmessage)] = '\0';
printf("ciphertext: %s", codedmessage);
printf("\n");
return 0;
}
}
// function assesses if the key input is valid and returns 0 if it is and 1 if it is not
int get_validkey(string Text)
{
int inputlength = strlen(Text);
if (inputlength != 26)
{
return 1;
}
else
{
for (int g = 0; g < 26; g++)
{
// checks if the character is non alphabetical
char chartest = toupper(Text[g]);
if (chartest < 'A' || chartest > 'Z')
{
return 2;
}
// scans all characters before A[g] to see if it has already been used
for (int k = 0; k < g; k++)
{
char beforechar = toupper(Text[k]);
if (chartest == beforechar)
{
return 3;
}
}
}
return 0;
}
}
int get_Alpha_to_code(char charac)
{
// define order for alphabet
const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char Alphachar = '\0';
// look at char position in alphabet
char chartest = toupper(charac);
// find position of charac in chain
int k = 0;
while (chartest != Alphachar)
{
Alphachar = Alphabet[k];
k++;
}
// send back char in code
return k - 1;
}
Check results are:
:) substitution.c exists :) substitution.c compiles :( encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key output not valid ASCII text
:( encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
output not valid ASCII text
:( encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
output not valid ASCII text
:( encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
output not valid ASCII text
:) encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
:) encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
:) encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
:) encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
:) handles lack of key :) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key
My results seems working because for 'A' I've 'Z', for 'a' I've got 'z', ...
But check system does not recognize my output as ASCII.
your problem is in that line :
codedmessage[strlen(codedmessage)] = '\0';
it can be
codedmessage[i] = '\0';
or
codedmessage[charcount] = '\0';
or more expensive for nothing
codedmessage[strlen(tocode)] = '\0';
because you cannot use strlen on codedmessage before to put the null character ending it (which is what you are trying to do), so you (may be) rewrite 0 exactly at the position you (may be) found it, that can be out of codedmessage and the behavior is undefined. In the next line printf writes the chars until it (may be) found a null char, so writing non initialized characters from it, the behavior is again undefined
Out of that, in :
int charcount = strlen(tocode);
char codedmessage[charcount];
for (int i = 0; i < strlen(tocode); i++)
you know the length is charcount why do you callstrlen(tocode) each turn knowing it is unchanged and what is its value ?
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<ctype.h>
#include<string.h>
int main(int argc, string argv[]){
int k,j,i=0,ch,pos;
bool apha=true;
string in = GetString();
int num = strlen(in);
for(int z=0;z<strlen(argv[1]);z++){
if(!isalpha(argv[1][z])){
apha=false;
}
}
if(argc!=2||!apha){
printf("Dude we only accept alphabets...");
return 1;
}
string key = argv[1];
int keylength = strlen(key);
for (i=0,j=0;i<num;i++,j++){
if(isupper(key[i])){
k=key[j%keylength]-'A';
}
if(islower(key[i])){
k=key[j%keylength]-'a';
}
if(isupper(in[i])){
pos=in[i]-'A';
ch = ((pos + k)%26) + 'A';
printf("%c",ch);
}
if(islower(in[i])){
pos=in[i]-'a';
ch = ((pos + k)%26) + 'a';
printf("%c",ch);
}
if(isspace(in[i])){
printf(" ");
}
if(ispunct(in[i])){
printf("%c",in[i]);
}
}
printf("\n");
}
Output condition checks:
:) vigenere.c exists
:) vigenere.c compiles
:) encrypts "a" as "a" using "a" as keyword
:( encrypts "world, say hello!" as "xoqmd, rby gflkp!" using "baz" as keyword
\ expected output, but not "xoqkj, yfd gfllp!\n"
:( encrypts "BaRFoo" as "CaQGon" using "BaZ" as keyword
\ expected output, but not "CaQEun\n"
:( encrypts "BARFOO" as "CAQGON" using "BAZ" as keyword
\ expected output, but not "CAQEON\n"
:( handles lack of argv[1]
\ expected output, not a prompt for input
:( handles argc > 2
\ expected output, not a prompt for input
:( rejects "Hax0r2" as keyword
\ expected output, not a prompt for input
What is wrong with my code? I have scrutinized the logic and the error seems to be in the way the key has been wrapped, though I could not find any errors. Where have I gone wrong?
There are several problems with your code:
You're error checking isn't correct. You check if(argc!=2||!apha) after you've already evaluated strlen(argv[1]) -- by then it's too late! Check the validity of argc before accessing argv and don't double up the argument count error and alphabetic key error, they're independent. Also, error messages should go to stderr, not stdout.
You're completely mishandling the key indexing. As #Bob__ noted, the indexing in this code:
if(isupper(key[i])){
k=key[j%keylength]-'A';
}
needs to be consistent
if (isupper(key[j % keylength])) {
k = key[j % keylength] - 'A';
}
But also, you're not incrementing j correctly, you have it tracking i:
for (i=0,j=0;i<num;i++,j++){
Instead, i should increment for every character in the input string, j should increment for every encryptable letter in the input string.
Reworking your code to fix the above errors and general style issues, we get something like:
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
int main(int argc, string argv[]) {
if (argc != 2) {
fprintf(stderr, "Please supply an encryption key.\n");
return 1;
}
string key = argv[1];
int key_length = strlen(key);
bool is_alpha = true;
for (int z = 0; z < key_length; z++) {
if (!isalpha(key[z])) {
is_alpha = false;
}
}
if (!is_alpha) {
fprintf(stderr, "Sorry, we only accept alphabetic keys.\n");
return 1;
}
string in = GetString();
size_t length = strlen(in);
for (int i = 0, j = 0; i < length; i++) {
if (isalpha(in[i])) {
int ch, k = key[j++ % key_length];
if (isupper(k)) {
k -= 'A';
} else {
k -= 'a';
}
if (isupper(in[i])) {
int pos = in[i] - 'A';
ch = ((pos + k) % 26) + 'A';
} else {
int pos = in[i] - 'a';
ch = ((pos + k) % 26) + 'a';
}
printf("%c", ch);
} else if (isspace(in[i])) {
printf(" ");
} else if (ispunct(in[i])) {
printf("%c", in[i]);
}
}
printf("\n");
return 0;
}
USAGE SIMULATION
> ./a.out baz
world, say hello!
xoqmd, rby gflkp!
>
Here is the answer as I got it-
-The most striking error is probably the:
if(isupper(key[i])){
k=key[j%keylength]-'A';
}
It should check for the corresponding character so should check for:
if (isupper(key[j % keylength])) {
k = key[j % keylength] - 'A';
}
-Also, the increment of the key-increment is important, to do that just increment only if it is an alphabet. So an isalpha check for that is needed (as you don't want the character to change even for a space).