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"
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;
}
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;
}
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++]);
...
The function of the program is to be run with a command-line argument, for example, might be the string NQXPOMAFTRHLZGECYJIUWSKDVB. This 26-character key means that A (the first letter of the alphabet) should be converted into N (the first character of the key), B (the second letter of the alphabet) should be converted into Q (the second character of the key), and so forth. A message like HELLO, then, would be encrypted as FOLLE, replacing each of the letters according to the mapping determined by the key.
e.g.
./substitution JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext: HELLO
ciphertext: VKXXN*
After starting the program with a valid command-line argument, the program doesn't output anything. It should output the ciphertext which has been encrypted using the key. I can't seem to find where the logic error is.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
bool is_valid_key(string plaintext);
int main(int argc, string argv[])
{
//Error message if user inputs incorret comand-line argument
if (argc != 2)
{
printf("Usage: ./substitution key\n");
return 1;
}
if (!is_valid_key(argv[1]))
{
printf("Key must contain 26 characters.\n");
return 1;
}
//Prompts user for plaintext
string plaintext = get_string("Plaintext: ");
string difference = argv[1];
for (int i = 'A'; i < 'Z'; i++)
{
difference[i - 'A'] = toupper(difference[i - 'A']) - i;
}
printf("Ciphertext: ");
for (int i = 0, len = strlen(plaintext); i < len; i++)
{
if(isalpha(plaintext[i]))
{
plaintext[i] = plaintext[i] + difference[plaintext[i] - (isupper(plaintext[i]) ? 'A' : 'a')];
}
printf("%c", plaintext[i]);
}
printf("\n");
}
//Checks vailidity of the key
bool is_valid_key(string plaintext)
{
int len = strlen(plaintext);
if (len != 26)
{
return false;
}
int freq[26] = { 0 };
for (int i = 0; i < len; i++)
{
if (!isalpha(plaintext[i]))
{
return false;
}
int index = toupper(plaintext[i]) - 'A';
if (freq[index] > 0)
{
return false;
}
freq[index]++;
}
return true;
}
Line 25 should have been for (int i = 'A'; i <= 'Z'; i++).
Line 29 should have been printf("ciphertext: ");
I coded the assignment and followed the best that I could. I can pass all of the Check50 arguments except one! Help?? The validation is correct, but when I run the debugger, it begins to give me problems around the ciphering section. Thank you!
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int arg, string argv[])
{
//KEY VALIDATION
if (arg != 2) //checks to make sure that arg is 2 (for # of arguments)
{
printf("Usage: ./caesar key\n");
return 1;
}
else if (arg == 2)
{ // checks key for validity
for(int i = 0, len = strlen(argv[1]); i < len; i++)
{
if (!isdigit(argv[1][i]))
{
printf("Usage: ./caesar key\n");
return 1;
}
}
//int key = atoi(argv[1]);
}
//ciphering
int key = atoi(argv[1]);
string plain = get_string("plaintext: ");
int len_plain = strlen(plain);
string cipher = plain;
for (int x = 0; x < len_plain; x++)
{
if (plain[x] >= 'a' && plain[x] <= 'z')
{
cipher[x] = ((plain[x] + key)%122);
if (cipher[x]<97)
{
cipher[x] = cipher[x] + 96;
}
}
else if (plain[x] >= 'A' && plain[x] <= 'Z')
{
cipher[x] = ((plain[x] + key)%90);
if (cipher[x] < 65)
{
cipher[x] = cipher[x] + 64;
}
}
else
{
cipher[x] = plain[x];
}
}
printf("ciphertext: %s\n", cipher);
}
the check50 results I keep getting are,
:) 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
output not valid ASCII text
printf("ciphertext: ");
for (int x = 0, y = len_plain; x < y; x++)
{
//Use islower and isupper instead of typing so much words
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);
}
//If neither then just print it
else
{
printf("%c", code[i]);
}
}
printf("\n")