My code for CS50 pset2 Vigenere cypher is as follows. I am new to C programming.
[ I edited the code once after I got some suggestions and this code(below) is my new edited code.]
When I run the code it produces infinite loop and also new encrypted text is not produced as it is supposed to be. Can I please get some suggestions and advice regarding the correction of my code ?
Thank 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) //if it is not rqual to 2, it gives an error message.
{
printf("Enter the valid input : \n");
return 1;
}
if (argc == 2) //if two commands are given then it proceeds to other step.
{
string k = argv[1];
string m = GetString();
int l = strlen(k);
int p = strlen(m);
for( int i = 0; i <= p ; i++ ) //it has to keep on rotating from 0 to len of string and back to zero and so on.
{
{
i = i % l;
}
if (isalpha(m[i]) && isalpha(k[i])) // it proceeds ahead only if the input given is an alphabet, if the input is sth other than alphabet it prints exactly as it is.
{
for(int t = 0; t <= p ; t++)
{
if(isupper(m[t])) // when is it capital letter.
{
printf("%c", ( m[t] - 65 + k[i]) % 26 + 65);
}
if(islower(m[t])) // when it is small letter.
{
printf("%c" , ( m[t] - 97 + k[i])% 26 + 97);
}
}
}
else //if it is not an alphabet it returns as it is.
{
printf("%c", m[i]);
}
}
}
printf("\n");
return 0;
}
Let's look at the error. It says that the parameter you gave there is not an array, while you are using it as an array. And that's right : p is an integer, and not an array :
int p = strlen(msg);
Using p[i] means that you want to access the element number i of your p array. But it is impossible to reach this value, because p is simply an integer variable, and not an array.
What you probably wanted to use as an array was one of your string parameters, key or msg. A string variable in CS50 is the equivalent of a char * variable in classic C, and is used as an array of characters.
Related
My code seems to be working properly except at the point when it should print the final output. The problem is to input a string and output an encrypted version. The encryption works by adding an int defined as the key and then adding that value to each character of the ascii values of the inputed string. My issue is that when the cypher text is outputted there are only spaces and no letters or even numbers.
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[]) {
int key = atoi(argv[1]);
printf("%i\n", key);
if (argc != 2) {
printf("Usage: ./ceasar key\n");
} else {
string text = get_string("Plaintext: ");
for (int i = 0, len = strlen(text); i < len; i++) {
int cipher = text[i];
int ciphertext = cipher + key;
int ciphermod = ciphertext % 26;
printf("%c", ciphermod);
}
printf("\n");
}
}
You've got a few issues going on here. Please make sure to thoroughly read the assignment before turning to others for assistance.
The assignment requires you to:
Only encode alphabetic characters. Look to the function isalpha() for this.
Encode both uppercase and lowercase characters accurately. Note that, in ASCII, uppercase letters and lowercase letters are separate entities.
Meaning, you must have your code be able to handle both, as they are each handled differently.
Perhaps taking some time to sit and take in the ASCII table may be helpful to you, as it will help you understand what is really happening when you add the key.
Use the correct formula for encoding letters. The i'th ciphered letter ci corresponding to the i'th plaintext letter pi is defined as ci = (pi + k) % 26.
Your code is equivalent to this formula, but it does not account for wrapping, uppercase/lowercase letters, etc. The project specification doesn't just ask you to repeat the formula, it asks you to solve a problem using it. To do so, you must understand it. I explain more, subsequently.
I recommend:
Modifying the text in-place. Currently, you calculate the ciphered text and print it. If you add code for modifying the text where it sits, it'll make ignoring non-alphabetic characters easier.
Modify the formula.
Where 𝚨 is the ASCII character code for the beginning of either the uppercase or lowercase characters, the formula might shake out as follows:
ci = (pi - 𝚨 + k) % 26 + 𝚨
What this modified formula does is first take the ASCII code for Pi and turn it into a number that represents which letter in the alphabet it is, ignoring case. Then, you can add the key(shift the cipher). Using % 26 on this result then makes sure that the result is between 1 and 26—always a letter. Finally, we add back 𝚨 so that the character has a case again.
Here's the modified code with the solution broken down, step by step:
// ...
for (int i = 0, n = strlen(text); i < n; i++) {
if (!isalpha(text[i])) continue;
if (isupper(text[i])) {
// the letter's ASCII code on its own.
int charcode = text[i];
// the letter's index in the alphabet. A = 0, B = 1, etc.
// this is no longer a valid ASCII code.
int alphabet_index = charcode - 'A';
// the letter's index in the alphabet, shifted by the key.
// note, this may shift the letter past the end/beginning of the alphabet.
int shifted_alphabet_index = alphabet_index + key;
// the letter's index in the alphabet, shifted by the key, wrapped around.
// the modulo operator (%) returns the remainder of a division.
// in this instance, the result will always be between 0 and 25,
// meaning it will always be a valid index in the alphabet.
int shifted_index_within_alphabet = shifted_alphabet_index % 26;
// this is the final ASCII code of the letter, after it has been shifted.
// we achieve this by adding back the 'A' offset so that the letter is
// within the range of the correct case of letters.
int final_shifted_charcode = shifted_index_within_alphabet + 'A';
text[i] = final_shifted_charcode;
}
else { // islower
int charcode = text[i];
int alphabet_index = charcode - 'a';
int shifted_alphabet_index = alphabet_index + key;
int shifted_index_within_alphabet = shifted_alphabet_index % 26;
int final_shifted_charcode = shifted_index_within_alphabet + 'a';
text[i] = final_shifted_charcode;
}
}
printf("ciphertext: %s\n", text);
// ...
And here is the solution, simplified down:
// ...
for (int i = 0, n = strlen(text); i < n; i++) {
if (!isalpha(text[i])) // if not alphabetic, skip
continue; //
if (isupper(text[i])) // if uppercase
text[i] = (text[i] - 'A' + key) % 26 + 'A'; //
else // if lowercase
text[i] = (text[i] - 'a' + key) % 26 + 'a'; //
}
printf("ciphertext: %s\n", text);
// ...
Just as a side note, the statement if (!isalpha(text[i])) is acting like something called a guard clause. This is a useful concept to know. Using guard clauses allows you to have simpler, more readable code. Imagine if I had nested all of the code inside the for loop under the if (isalpha(text[i])) condition. It would be harder to read and understand, and difficult to match up the different bracket pairs.
Edit: I would also echo what chqrlie said. Do not use argv[n] until you have verified that argc >= (n + 1)
The formula to compute the ciphered characters is incorrect:
you should only encode letters
you should subtract the code for the first letter 'a' or 'A'
you should add the code for the first letter 'a' or 'A' to the encoded index.
Note also that you should not use argv[1] until you have checked that enough arguments have been passed.
Here is a modified version:
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, string argv[]) {
if (argc != 2) {
printf("Usage: ./ceasar key\n");
} else {
int key = atoi(argv[1]);
printf("%i\n", key);
string text = get_string("Plaintext: ");
for (int i = 0, len = strlen(text); i < len; i++) {
int c = text[i];
if (c >= 'a' && c <= 'z') {
int cipher = c - 'a';
int ciphertext = cipher + key;
int ciphermod = ciphertext % 26;
c = 'a' + ciphermod;
} else
if (c >= 'A' && c <= 'Z') {
int cipher = c - 'A';
int ciphertext = cipher + key;
int ciphermod = ciphertext % 26;
c = 'A' + ciphermod;
}
printf("%c", c);
}
printf("\n");
}
return 0;
}
I am a beginner trying to learn to code.
Currently I am doing the CS50 course. I have encountered a problem with the Vigenere cipher problem; please see my code on a github link below.
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ASCII_VALUE_LOWER 97
#define ASCII_VALUE_UPPER 65
#define NR_OF_LETTERS 26
int main(int argc, string argv[])
{
char key[strlen(argv[1]) + 1];
strcpy(key, argv[1]);
int keyLen = strlen(key);
for (int k = 0; k < keyLen; k++)
{
if (!isalpha(key[k]))
{
printf("ERROR: Secret key has to be alphabetical string, program will be terminated now!\n");
return 1; // main should return 1 (signify an error)
}
//converting key letters to respective values
if (isupper(key[k]))
{
key[k] -= ASCII_VALUE_UPPER;
}
key[k] -= ASCII_VALUE_LOWER;
}
//if program is executed without an argument, or with more than one arguments
if (argc != 2)
{
printf("ERROR: You need to give a secret key as an argument, program will be terminated now!\n");
return 1; // main should return 1 (signify an error)
}
else
{
string plaintext = get_string("plaintext: "); //get a plaintext from a user using cs50 custom function from their library
int stringLen = strlen(plaintext);
int keyIndex = 0;
for (int j = 0; j < keyLen; j++)
{
}
//for each character in the plaintext string
for (int i = 0; i < stringLen; i++)
{
//check if is alphabetic (tolower, toupper)
if (isalpha(plaintext[i]))
{
//cypher_character = (plain_character + key_character)% 26
if (islower(plaintext[i]))
{
keyIndex %= keyLen;
plaintext[i] = ((plaintext[i] - ASCII_VALUE_LOWER + key[keyIndex]) % NR_OF_LETTERS) + ASCII_VALUE_LOWER;
}
else
{
plaintext[i] = ((plaintext[i] - ASCII_VALUE_UPPER + key[keyIndex]) % NR_OF_LETTERS) + ASCII_VALUE_UPPER;
}
keyIndex++;
}
//else leave as is
}
//print ciphertext in a format "ciphertext: " + ciper
printf("ciphertext: %s\n", plaintext);
return 0;
}
}
The issues are as following:
if you pass an argument in uppercase letters in a key, the values are weird and the conversion doesn't work. The idea is to take each character in a string key and subtracting 65 if uppercase, or 97 if lowercase, so their ASCII value would go to 0 - 25. I then can make a use of them in a Vigenere cipher formula: cipher[i_index] = (plaintext[i_index] + key[j_index]) % 26
doesn't handle lack of argv[1], even though there is an IF condition (!argc == 2), so it shouldn't go through if you don't pass anything.
"failed to execute program due to segmentation fault".
I have tried everything in my capability, I am pretty tired, maybe tomorrow the solution will popout instantly.
I ask you to give me some hints, possibly not revealing everything, but maybe guiding me, so I can learn from it.
if (isupper(key[k]))
{
key[k] -= ASCII_VALUE_UPPER;
}
key[k] -= ASCII_VALUE_LOWER;
If the character is upper case, this subtracts ASCII_VALUE_UPPER. And then, no matter what, it subtracts ASCII_VALUE_LOWER. From the surrounding code, I assume you meant:
if (isupper(key[k])) {
key[k] -= ASCII_VALUE_UPPER;
} else {
key[k] -= ASCII_VALUE_LOWER;
}
As others suggested, everything was fixed, if someone would be curious where exacly was the mistake:
1.] Mistake pinpointed by #Rob Napier
for (int i = 0; i < stringLen; i++)
{
//check if is alphabetic (tolower, toupper)
if (isalpha(plaintext[i]))
{
keyIndex %= keyLen; // makes sure that keyIndex doesnt exceeds actual string length
//cypher_character = (plain_character + key_character)% 26
if (islower(plaintext[i]))
{
plaintext[i] = ((plaintext[i] - ASCII_VALUE_LOWER + key[keyIndex]) % NR_OF_LETTERS) + ASCII_VALUE_LOWER;
}
else
{
plaintext[i] = ((plaintext[i] - ASCII_VALUE_UPPER + key[keyIndex]) % NR_OF_LETTERS) + ASCII_VALUE_UPPER;
}
keyIndex++;
}
//else leave as is
}
keyIndex %= keyLen; was placed inside of the if condition below it, so it didnt execute in every iteration of the FOR loop.
2.] answered by #Joe Farrel:
because by the time I checked whether argc != 2, I have already accessed argv[1], and thereby assumed that argc >= 2. If the caller hasn't supplied an argument, then argv[1] is past the end of the array and evaluating that expression results in undefined behavior. - So I moved the if condition as a first thing in main.
I'm having trouble writing the last part of my code for an assignment which involves writing a Vigenere cipher. The encryption part is working fine, but I'm having trouble figuring out how to repeat the encryption word/keyword. So it works fine if the message that needs to be encrypted is smaller or equal to the keyword and otherwise it puts out another couple of characters, that seem encrypted, but aren't.
This is the code so far:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("YELL!\n");
return 1;
}
//check if the number of command line arguments is correct, if not, YELL!
string keyword = (argv[1]);
//get keyword
for (int j = 0, n = strlen(keyword); j < n; j++)
{
if(!isalpha(keyword[j]))
{
printf("YELL!\n");
return 1;
}
}
//check if the keyword is only alphabetical, if not, YELL!
string message = GetString();
//get plaintext
for (int j = 0, n = strlen(keyword); j < n; j++)
{
if (isupper(keyword[j]))
{
keyword[j] = (keyword[j] - 'A');
}
if (islower(keyword[j]))
{
keyword[j] = (keyword[j] - 'a');
}
}
//this is to get the numerical values from the ascii values of the keyword.
for (int i = 0, j = 0, n = strlen(message); i < n; i++, j++)
//counting through the message & the cypher
{
if (isalpha(message[i]))
{
if (isupper(message[i]))
{
message[i] = (((message[i] - 'A') + keyword[j]) % 26 + 'A');
}
if (islower(message[i]))
{
message[i] = (((message[i] - 'a') + keyword[j]) % 26 + 'a');
}
//adding a keyword value [j] to message [i] and converting back to ascii value,
//individually for upper and lowercase characters.
}
printf("%c", message[i]);
}
}
It's probably an easy solution, but I just can't figure it out. Any help would be vastly appreciated!
It's a miracle encryption is working for you. I think is is not, as your loop is clearly might get j past the keyword length and then keyword[j] will be out of bounds and exhibit undefined behavior. You need only to iterate on i over the message length and index the keyword with keyword[i % strlen(keyword)], such that the index will go cyclically from 0 to the length of the keyword minus one.
This question already has an answer here:
C program printing weird characters
(1 answer)
Closed 8 years ago.
I have written this simple program for an assignment, but when I input my text, the output gives me symbols instead of chars. any help would be appreciated. I do not know why my output appears that way, but the program seems to compile fine. Maybe it is working and I need to just do a base test with the math to see if it is functioning properly. In any event if anyone sees errors in this, feedback is much appreciated.
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <ctype.h>
string Crypto(string, int); // rotation
int main(int argc, string argv[])
{
int k = 0;
// error checing
if (argc == 0 || argc == 1 || argc > 2)
{
// get mad
printf("Enter 1 integer as an argument. Stop messing around!\n Try Again: ");
return 1;
}
else
{
//create command line arguments to be stored into k
k = atoi(argv[1]);
k = k * 1;
}
// Get text to be encrypted
printf("Enter the text you want to encrypt: \n");
string a = GetString();
string b = Crypto(a, k);
printf("%s\n", b);
return 0;
}
//Now let's get cryptic
string
Crypto(string a, int k)
{
int c = 0;
for (int i = 0, n = strlen(a); i < n; i++)
{
if(a[i] >= 65 && a[i] <= 90)
{
c = ((26 - (91 - a[i] + k) % 26));
a[i] = c + 'A';
}
else
{
c = ((26 - (123 - a[i] + k % 26)));
a[i] = c + 'a';
}
}
return a;
}
In function Crypto, you need to attach a \0 (null) character to signify the end of the string. Just before return a;, write a a[i+1] = '\0'; statement.
My goal is to make a Vigenere cipher. I am attempting to do this by getting the key from argv, getting the string from the user, then passing the message and key though a function I made which combined them and returns the new value before printing it. For some reason it is just printing the key. I think it has something to do with the new function and how I am trying to use the returned value. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <cs50.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>
int new_crypt;
int encrypt(string , int );
int main(int argc, string argv[])
{
if( argc != 2)
{
printf("Plese enter only one key");
return 1;
}
string message = GetString();
for(int i=0; i < strlen(message); i++)
{
int klen = strlen(argv[1]);
//repeats the key until the message is over
int key= argv[1][i%klen];
bool kupper = isupper(key);
bool klower = islower(key);
bool kalpha = isalpha(key);
if(kupper == true){
//ASCII value of A is 65. 'A' = 0 shifts
int k = key-65;
int new_crypt = encrypt(message, k);
printf("%c", new_crypt);
}
if(klower == true){
//ASCII value of 'a' is 97. 'a' = 0 shifts
int k = key- 97;
int new_crypt = encrypt(message, k);
printf("%c", new_crypt);
}
if(kalpha == false){
int k = 0;
int i = i-1;
int new_crypt = encrypt(message, k);
printf("%c", new_crypt);
}
}
printf("\n");
return 0;
}
int encrypt(string message, int k)
{
for(int i=0; i < strlen(message); i++)
{
bool upper = isupper(message[i]);
if(upper == true)
{ //Makes sure the message doesnt go past 'Z'.. If it does it mod 90 it / // and adds 65 ('A')
int crypt = (message[i]+ k) % 90;
if(crypt < 65)
{
int new_crypt = (crypt + 65);
return new_crypt;
}
else{
int new_crypt = crypt;
return new_crypt;
}
}
bool lower = islower(message[i]);
if(lower == true)
{
int crypt = (message[i]+ k) % 123;
if(crypt < 97)
{
int new_crypt = crypt + 97;
return new_crypt;
}
else{
int new_crypt = crypt;
return new_crypt;
}
}
bool alpha = isalpha(message[i]);
if(alpha == false)
{
int new_crypt = message[i];
return new_crypt;
}
}
return 0;
}
The loop in the encrypt function is completely useless, because there is no path through the loop-body without a return statement causing the loop to be terminated and control returned to the caller of encrypt. This makes that the program as a whole repeatedly encrypts the first character of the message with successive elements from the key.
The easiest way around this is to make the following changes
Remove the loop from the encrypt function
Pass, as an additional argument, the element from the message that you want to encrypt, making the signature
int encrypt(string message, int k, int i)
Some miscellaneous remarks:
The global variable new_crypt is not used anywhere. You can safely remove it. (You should avoid the use of global variables as much as reasonably possible).
Instead of using the magic number 65, you can also use the character literal 'A'. This has the advantage that you don't need a comment to explain the number 65 and that it is always the correct value for the capital A, even if you end up not using ASCII. (But see also the next bullet)
Your code assumes the letters A to Z (and a to z) have contiguous values (such that Z == A+26). This may happen to be the case for the English alphabet in the ASCII encoding, but it is not guaranteed for other language alphabets or encodings.