Vigenere Cipher logic error - c

#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).

Related

CS50x - PSET2- Substitution

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.

CS50 Caesar - Converting from ASCII number to character

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

How do I write code to deny a non-numeric input using I've tried argv[1][i] it doesn't seem to work

I don't know where did I do wrong here my first time trying to use
int main(int argc, argv[])
I just don't know if I'm using it correctly.
Here's some code:
The code compiles properly and does give me the result correctly as well
encrypts "BaRFoo" as "FeVJss" using 4 as key
Log
running ./caesar 4...
sending input BaRFoo...
checking for output "ciphertext: FeVJss\n"...
BUT here is my problem: when I type in 2r or 4w or any non numeric key, I get this message:
:( handles non-numeric key
timed out while waiting for program to exit
Can anyone tell me how to add another loop for this function to get the numerical key to print as -Usage: ./caesar key . please ? Thanks very much an appreciate your help and useful advice.
#include <string.h>
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
// Variable declarations
bool keySuccessful = false;
int key = 0;
int input_length = 0;
string text = "" ;
// The number of command line args submitted was incorrect.
do
{
if(argc != 2)
{
printf("Usage: ./caesar key .\n");
return 1;
}
else
{
// Convert ASCII char to a alphabate char
// Access individual char in the string plain
// get the key val and converted to integer
key = atoi(argv[1]);
keySuccessful = true;
printf("%s",argv[1]);
}
}
while(!keySuccessful);
// get user input
text = get_string("%s",text);
printf("ciphertext: ");
input_length = strlen(text);
for (int i=0; i<input_length; i++)
{
// Checking if is the lowercase a=97 to z=112
// Print out lower case with ky a=65 to z=90
if(isupper(text[i]))
{
printf("%c", (((text[i] - 65) + key) % 26) + 65);
}
else if(islower(text[i]))
{
printf("%c", (((text[i] - 97) + key) % 26) + 97);
}
else
{
printf("%c", text[i]);
}
}
printf("\n");
return 0;
}
If I understood your question correctly, you want to make sure the first argument is a number, and stop execution otherwise.
You could easily do that looping over the argument, and checking the output of isdigit() from ctype.h for each character, like this:
int argumentIsNumber = 1;
for (int i = 0; i < strlen(argv[1]); i++) {
if (isdigit(argv[1][i]) == 0) {
argumentIsNumber = 0;
}
}
if (argumentIsNumber == 0) {
// Tell the user the introduced key is wrong
}
As you see in this example, you could set a flag to 1, and change it to 0 whenever a non-digit is found. Then, when the loop ends, you only have the flag to 1 if all the characters were digits.
Hope it helps!
you should make code if user prompt somethink like
$ ./caesar 20x
you shoud print
Usage: ./caesar key
for (int i = 0 , n = strlen(argv[1]); i < n; i++)
{
if (isalpha(argv[1][i]))
{
printf("Usage: ./caesar key\n");
return 1;
}
}

PSET 2: Vigenere Cipher partially working?

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.

What's wrong with my cs50 vigenere code? I am close on the output

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);
}
}

Resources