I completed the caesar assignment on cs50 and tested it on my terminal and it worked perfectly, but on check50 is kept failing some tests.
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
int getkey(string k);
string cipher(string s, int key);
int key;
int p;
int q;
int main(int argc, string argv[])
{
// Allow 2 command line inputs
if (argc == 2)
{
// Assign a local string to allow char scan
string s = argv[1];
// Check if all inputs are numbers
for (int i = 0; s[i] != 0; i++)
{
if (s[i] < 48 || s[i] > 57)
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// Get key from string
int cipherkey = getkey(s);
// Get user text
string text = get_string("plaintext: ");
// Calculate ciphertext and print
string ciphertext = cipher(text, cipherkey);
printf("ciphertext: %s\n", ciphertext);
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// Change string to int. Turns out theres already a function for this called atoi()
int getkey(string k)
{
key = 0;
for(int i = 0, conv = 0, n = strlen(k); k[i] != 0; i++, n--)
{
// Calcute the placevalue
p = pow(10, n-1);
conv = k[i] - 48; // Convert to int
key = key + (conv * p); // Sum up
}
return key % 26;
}
// Cipher text
string cipher (string s, int key)
{
for(int i = 0; s[i] != 0; i++)
{
if(islower(s[i]))
{
s[i] = s[i] + key;
while(s[i] > 122)
{
s[i] = (s[i] - 123) + 97;
}
}
else if(isupper(s[i]))
{
s[i] = s[i] + key;
while(s[i] > 90)
{
s[i] = (s[i] - 91) + 65;
}
}
}
return s;
}
with error message
:) caesar.c compiles.
:) encrypts "a" as "b" using 1 as key
:( encrypts "barfoo" as "yxocll" using 23 as key
output not valid ASCII text
:) 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]
:) handles non-numeric key
:) handles too many arguments
I wrote the code without knowing the "atoi" function so i implemented a function called getkey() to return key. when i returned key normally, it failed.
:( encrypts "barfoo" as "onesbb" using 65 as key
Output not a valid ASCII text
Until i returned key % 26;
I dont know why check50 isnt working although the program works well on my terminal. Please help.
Updated code:
#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
string cipher(string s, int key);
int main(int argc, string argv[])
{
// Allow 2 command line inputs
if (argc == 2)
{
// Assign a local string to allow char scan
string s = argv[1];
// Check if all inputs are numbers
for (int i = 0; s[i] != 0; i++)
{
if (s[i] < 48 || s[i] > 57)
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// Get key from string
int cipherkey = atoi(s);
// Get user text
string text = get_string("plaintext: ");
// Calculate ciphertext and print
string ciphertext = cipher(text, cipherkey);
printf("ciphertext: %s\n", ciphertext);
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// Cipher text
string cipher (string s, int key)
{
for(int i = 0; s[i] != 0; i++)
{
if(islower(s[i]))
{
s[i] = (int) s[i] + key;
while(s[i] > 'z')
{
s[i] = (s[i] - 123) + 97;
}
}
else if(isupper(s[i]))
{
s[i] = (int) s[i] + key;
while(s[i] > 'Z')
{
s[i] = (s[i] - 91) + 65;
}
}
}
return s;
}
I've rewritten the function trying to avoid going to far making corrections and improvements. Now, the copy is actually unnecessary, but I hope it helps you understand that a "signed (8 bit) char" is not 'wide' enough to use in calculations that lead to overflow...
Please read the following and try to follow along.
string cipher( string s, int key ) {
for( int i = 0; s[i] != '\0'; i++ ) {
if( !isalpha( s[i] ) )
continue;
int copy = (int)s[i]; // unnecessary casting, but...
// transform ASCII value into 0-25 range
if( islower( s[i] ) )
copy = copy - 'a';
else // must be uppercase
copy = copy - 'A';
copy = (copy + key) % 26;
// transform enciphered MODULO back into ASCII char
if( islower( s[i] ) )
copy = copy + 'a';
else // must be uppercase
copy = copy + 'A';
s[i] = copy;
}
return s;
}
Quiet night, so I thought I'd rewrite the rewrite just to see what it looked like...
string cipher( string s, int key ) {
for( char *cp = s; *cp; cp++ )
if( isalpha( *cp ) ) {
char c = "Aa"[ !!islower(*cp) ];
*cp = (char)(((*cp - c + key) % 26) + c);
}
return s;
}
Related
I've written the below code for the Caesar project in CS50 and I felt like it was hitting the brief, but when I have run the check, both of the checks containing uppercase letters are coming back incorrect There seems to be some extra characters printed between each of the uppercase letters that don't appear when I run the code myself.
Am I missing something in my code that's causing this to happen?
the message from Check50
:( encrypts "BARFOO" as "EDUIRR" using 3 as key
expected "ciphertext: ED...", not "ciphertext: E\..."
:( encrypts "BaRFoo" as "FeVJss" using 4 as key
expected "ciphertext: Fe...", not "ciphertext: F\..."
my code
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
bool only_digits(string s);
char rotate(int, int);
int main(int argc, string argv[])
{
//make sure key is entered correctly
{
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
else
{
int cont = only_digits(argv[1]);
if (cont != 1)
{
return 1;
}
else
{
// convert key from string to int
int key = atoi(argv[1]);
string plain = get_string("plain text: \n");
// rotate the letters using the key
printf("ciphertext: ");
{
for (int i = 0, len = strlen(plain); i < len; i++)
{
rotate(plain[i], key);
}
}
printf("\n");
}
}
}
}
//rotate the letters using the key
char rotate(int p, int i)
{
// rotate uppers
if isupper(p)
{
p = p - 65;
char c = (p + i) % 26;
c += 65;
printf("%c", c);
}
// rotate lowers
if islower(p)
{
p = p - 97;
char c = (p + i) % 26;
c += 97;
printf("%c", c);
}
// if its a character keep the same
else
{
printf("%c", p);
}
return 0;
}
//make sure key entered is only digits
bool only_digits(string s)
{
for (int i = 0, len = strlen(s); i < len; i++)
{
if (!isdigit(s[i]))
{
printf("Usage: ./caesar key\n");
return 0;
}
else
{
;
}
}
return 1;
}
While I am unsure if it's your only bug, you forgot an else if in your rotate function such that upper case letters would print in the first case but also the else to the second case after they had been modified.
char rotate(int p, int i)
{
// rotate uppers
if isupper(p)
{
p = p - 65;
char c = (p + i) % 26;
c += 65;
printf("%c", c);
}
// rotate lowers
else if islower(p) // without else if here, upper cases would fall into the else below with p - 65
{
p = p - 97;
char c = (p + i) % 26;
c += 97;
printf("%c", c);
}
// if its a character keep the same
else
{
printf("%c", p);
}
return 0;
}
This problem set requires us to code a programme that takes a key from the user from the CLI and then an input (plaintext) and return a ciphertext version that is scrambled based on the key provided.
My code returns the correct ciphertext given any key and plaintext, however, the apparent output when using the in-built check50 module from cs50 is "", an empty string.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
string encrypt(string keys, string inputs);
char newkey;
string ciphertext;
int main(int argc, string argv[])
{
if (argc == 1)
{
printf("Please enter a key!\n");
return 1;
}
if (argc > 2)
{
printf("You can only have one key. The key must not have any spaces!\n");
return 1;
}
if (0 < strlen(argv[1]) && strlen(argv[1]) < 26)
{
printf("Key must contain 26 characters!\n");
return 1;
}
for (int j = 0; j < strlen(argv[1]); j++)
{
if (!((argv[1][j] >= 'a' && argv[1][j] <= 'z') || (argv[1][j] >= 'A' && argv[1][j] <= 'Z')))
{
printf("Key must contain alphabets only!");
return 1;
}
}
for (int k = 0; k < strlen(argv[1]); k++)
{
for (int l = (k + 1); l < strlen(argv[1]); l++)
{
if (argv[k] == argv[l])
{
printf("There can be no duplicate alphabets in the key!");
return 1;
}
}
}
string key = argv[1];
string input = get_string("plaintext: ");
encrypt(key, input);
printf("ciphertext: %s\n", ciphertext);
}
string encrypt(string keys, string inputs)
{
char ciphertexts[strlen(inputs)];
for (int i = 0; i < strlen(inputs); i++)
{
if (islower(inputs[i]))
{
int index = inputs[i] - 97;
newkey = keys[index];
ciphertexts[i] = tolower(newkey);
}
else if (isupper(inputs[i]))
{
int index = inputs[i] -65;
newkey = keys[index];
ciphertexts[i] = toupper(newkey);
}
else
{
ciphertexts[i] = inputs[i];
}
}
ciphertext = ciphertexts;
printf("%s\n",ciphertexts);
printf("%s\n",ciphertext);
return ciphertext;
}
The errors are as follows:
:( encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
expected "ciphertext: Z\...", not ""
This means that using a key of ZYXWVUTSRQPONMLKJIHGFEDCBA, and a plaintext of "A", "Z" is expected, but my programme outputs "".
I have a printf statement printing the cipher text, but it is somehow not captured
Memory management
Rather than return a pointer to a local variable (which is no longer valid after function ends #kaylum), pass into encrypt() a pointer to where to form the encrypted string.
Don't forget about '\0'
In forming the encrypted string account for the null character. #kaylum
Avoid repeated calls to strlen(inputs)
Advanced: Avoid negative ch with is...(ch)
Along with some other ideas, perhaps:
#include <ctype.h>
void encrypt(char *ciphertexts, const char *keys, char *inputs) {
// Let's work with unsigned char to avoid trouble negative char
const unsigned char *ukeys = (const unsigned char*) keys;
const unsigned char *uinputs = (const unsigned char*) inputs;
unsigned char *uciphertexts = (unsigned char*) ciphertexts;
size_t i = 0;
// Code uses a do loop to catch the null character.
do {
if (islower(uinputs[i])) {
int index = uinputs[i] - 'a';
unsigned char newkey = ukeys[index];
uciphertexts[i] = tolower(newkey);
} else if (isupper(uinputs[i])) {
int index = uinputs[i] - 'A';
unsigned char newkey = ukeys[index];
uciphertexts[i] = toupper(newkey);
} else {
uciphertexts[i] = uinputs[i];
}
} while (uinputs[i++]);
}
Code still has trouble when the locale is not "C" or with non-ASCII, but we can leave those as advanced issues.
Advanced
Since strings to encrypt can be lengthy, consider pre-forming a key valid for all char. I.e.
unsigned char keys256[UCHAR_MAX + 1] = { 0 };
for (i = 0; i <= UCHAR_MAX; i++) {
keys256[i] = i;
}
for (i = 0; key[i]; i++) {
keys256['a' + i] = tolower(key[i]);
keys256['A' + i] = toupper(key[i]);
}
and then in encrypt(..., keys256, ...), use a greatly simplified loop.
...
do {
uciphertexts[i] = keys256(uinputs[i]);
} while (uinputs[i++]);
...
Code compiles ok. I tried to use a debugger but couldn't figure out the problem.
Each iteration "c" is being filled, but it seems like the way I try to assign this variable "c", into the array nothing happens, so that the end print statement ends up "ciphertext: EMPTY NOTHING"
Any help or ideas very welcomed. I'm taking courses and trying to solve a problem set. I'm only begginer, pls do not judje hard :)
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <ctype.h>
int main (int argc, string argv[])
{
if(argc < 2 || 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]))
{
printf("Usage: ./caesar key \n");
return 1;
}
}
}
int key = atoi(argv[1]);
string initial_text = get_string("plaintext: ");
int cipher[strlen(initial_text)];
int i = 0;
int n = strlen(initial_text);
for (i = 0; i < n; i++)
{
int c = 0;
if (isupper(initial_text[i]))
{
c = (((int)initial_text[i] - 65 + key) % 26) + 65;
cipher[i] += c;
}
else if (islower(initial_text[i]))
{
c = (((int)initial_text[i] - 97 + key) % 26) + 97;
cipher[i] += c;
}
else
{
c=initial_text[i];
cipher[i] += c;
}
}
//////////////////
printf("ciphertext: %c\n", (char)cipher);
}
There are a number of issues:
Using int for cipher when you [probably] want char.
Not defining enough space in cipher for the trailing 0x00 [EOS] string terminator
Not setting the EOS at the end.
Using %c instead of %s in your printf.
if ((argc < 2) || (argc < 2)) is wrong and can be replaced with if (argc != 2).
Because that if does return, the subsequent else if check is no longer needed.
"hardwiring" decimal values (e.g. 65 for 'A').
Using strlen in the condition expression in a for loop. This increases the running time of the loop from O(n) to O(n^2).
This can be replaced with (e.g.) comparing the array element against 0x00.
Using strtol instead of atoi and checking the end can do decode and validation in a single operation.
Don't use string when defining argv -- use char **argv.
Your main lacks a return 0; at the end.
There's a lot of replicated code that can be simplified.
Here's the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// I don't have cs50 handy, so a quick hack ...
#define string char *
string
get_string(const char *prompt)
{
char buf[1000];
char *cp;
printf("%s",prompt);
fflush(stdout);
do {
cp = fgets(buf,sizeof(buf),stdin);
if (cp == NULL)
break;
// strip newline
buf[strcspn(buf,"\n")] = 0;
// IIRC, [real] get_string does heap allocation (vs. static buffer) but
// for demo purposes here, doesn't really matter
cp = strdup(buf);
} while (0);
return cp;
}
int
main(int argc, char **argv)
{
// check if there is exactly 2 arguments passing, otherwise prompt for a
// valid key(+return 1, to signal mistake)
if (argc != 2) {
printf("Usage: ./caesar key \n");
return 1;
}
// otherwise check if the key is only a digit and prompt for a valid
// key(+return 1, to signal mistake)
// loop through each character in the second argument and validate
char *arg = argv[1];
// convert a string to int variable to be used in transformation of letters
int key = strtol(arg,&arg,10);
if (*arg != 0) {
printf("Usage: ./caesar key \n");
return 1;
}
// prompt for plaintext
string initial_text = get_string("plaintext: ");
// create an array of int to store encrypted letters to then be converted
// in print by (char)
// NOTE: we need one extra to make room for the trailing EOS
char cipher[strlen(initial_text) + 1];
const char *src = initial_text;
char *dst = cipher;
for (int chr = *src++; chr != 0; chr = *src++, ++dst) {
if (isupper((unsigned char) chr)) {
*dst = (((chr - 'A') + key) % 26) + 'A';
continue;
}
if (islower((unsigned char) chr)) {
*dst = (((chr - 'a') + key) % 26) + 'a';
continue;
}
// for all none alphabetic symbols leave them as they are
*dst = chr;
}
// set the trailing EOS
*dst = 0;
// ////////////////
printf("ciphertext: %s\n", cipher);
return 0;
}
You could write for example
if ( argc < 2 || argc<2 || argc < 2 )
instead of
if ( argc < 2 || argc<2 )
and this will be correct but does not make a sense.
Just write
if ( argc != 2 )
{
puts( "Usage: ./caesar key" );
return 1;
}
This else statement
else if(argc == 2)
is redundant. Remove it.
Instead of this for loop
for (int i = 0; i < strlen(argv[1]); i++)
{
if(!isdigit(argv[1][i]))
{
printf("Usage: ./caesar key \n");
return 1;
}
}
it is simpler to write
char *p;
unsigned long ley = strtoul( argv[1], &p, 10 );
if ( *p || key == ULLONG_MAX )
{
puts( "Usage: ./caesar key" );
return 1;
}
There is no need to define a variable length array. You could change the original string pointed to by the pointer initial_text.
Introducing the variable n is redundant
int n = strlen(initial_text);
Instead of the loop
for (i = 0; i < n; i++)
you could use this loop
for ( size_t i = 0; initial_text[i] != '\0'; i++ )
Instead of these if statements
if (isupper(initial_text[i]))
and
else if (islower(initial_text[i]))
it will be more safer to write
if ( isupper( ( unsigned char )initial_text[i]))
and
else if ( islower( ( unsigned char )initial_text[i] ) )
Also using magic numbers like 65 makes the code unreadable. For example you could
write
c = ( initial_text[i] - 'A' + key ) % ( 'Z' - 'A' + 1 ) + 'A';
and
c = ( initial_text[i] - 'a' + key ) % ( 'z' - 'a' + 1) + 'a';
and instead of the compound assignment operator
cipher[i] += c;
you need to write
initial_text[i] = c;
Also in the call of printf you are using incorrect conversion specifier %c and casting
printf( "ciphertext: %c\n", (char)cipher);
As the string should be updated in place then the call of printf will look like
printf( "ciphertext: %s\n", initial_text );
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 trying to make Vigenere Cipher in C. https://www.youtube.com/watch?v=9zASwVoshiM this is info about Vigenere Cipher. My code works doesnt work for certain cases like encrypts "world, say hello!" as "xoqmd, rby gflkp!" using "baz" as keyword instead it encrypts it as xomd, szz fl. Another example is:
encrypts "BaRFoo" as "CaQGon" using "BaZ" as keyword but instead it encrypts it as CakGo. My code is given below please help me out:
#include<stdio.h>
#include<cs50.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
int main(int argc, string argv[]) {
//string plaintext;
string key;
if (argc != 2) {
printf("Please run the programme again this time using a command line argument!\n");
return 1;
}
key = argv[1];
int keys[strlen(key)];
for (int m = 0; m< strlen(key); m++) {
if (isalpha(key[m]) == false) {
printf("Re-Run The programme without any symbols.\n");
return 1;
}
}
for (int b = 0; b < strlen(key); b++) {
if (isupper(key[b]) == false) {
keys[b] = key[b] - 'a';
}
else {
keys[b] = key[b] - 'A';
}
}
//printf("Enter a string which should be encrypted: \n");
string plaintext = GetString();
int plength = strlen(plaintext);
int klength = strlen(key);
string ciphertext = key;
for (int u = 0; u<plength; u++) {
if (isalpha(plaintext[u]) == false) {
printf("%c", plaintext[u]);
continue;
}
int value = u % klength;
ciphertext[u] = (keys[value] + plaintext[u]);
if ((islower(plaintext[u])) && (ciphertext[u])>'z') {
ciphertext[u] = ciphertext[u] - 'z' + 'a' - 1;
}
if ((isupper(plaintext[u])) && (ciphertext[u])>'z') {
ciphertext[u] = ciphertext[u] - 'Z' + 'A' - 1;
}
printf("%c", ciphertext[u]);
}
printf("\n");
return 0;
}
string ciphertext = key;
ciphertext should start out as blank, it should not be set to key.
ciphertext[u] = ciphertext[u] - 'z' + 'a' - 1;
This is not correct because ciphertext[u] can go out of range. Character should be between 'a' to 'z'. Use the mod % operator to make sure character is within range. For example:
int j = 0;
for (int u = 0; u<plength; u++)
{
int c = plaintext[u];
if (isalpha(plaintext[u]))
{
int k = keys[j % klength];
if (islower(c))
{
c = 'a' + (c - 'a' + k) % 26;
}
else
{
c = 'A' + (c - 'A' + k) % 26;
}
j++;
}
ciphertext[u] = c;
}
printf("%s\n", ciphertext);
Lots of little issues (ciphertext needs to be allocated dynamically and start out as a copy of plaintext, not key; need to mod calculations to the length of the alphabet; incorrect calculations; print errors to stderr) and lots of little optimizations that can be made (combine loops; combine if clauses; save key length to a variable earlier). A rework of your code:
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[]) {
if (argc != 2) {
fprintf(stderr, "Please run the program again with a command line argument!\n");
return 1;
}
string key = argv[1];
int key_length = strlen(key);
int keys[key_length];
for (int i = 0; i < key_length; i++) {
if (!isalpha(key[i])) {
fprintf(stderr, "Re-run The program without any symbols.\n");
return 1;
}
keys[i] = toupper(key[i]) - 'A';
}
// printf("Enter a string which should be encrypted: \n");
string plaintext = GetString();
int text_length = strlen(plaintext);
string ciphertext = strcpy(malloc(text_length + 1), plaintext);
for (int i = 0, j = 0; i < text_length; i++) {
if (!isalpha(plaintext[i])) {
continue;
}
int index = j++ % key_length;
ciphertext[i] = (toupper(plaintext[i]) - 'A' + keys[index]) % (1 + 'Z' - 'A');
ciphertext[i] += isupper(plaintext[i]) ? 'A' : 'a';
}
printf("%s\n", ciphertext);
free(ciphertext);
return 0;
}
Encrypts "world, say hello!" as "xoqmd, rby gflkp!" with key "baz"
Encrypts "BaRFoo" as "CaQGon" with key "BaZ"