#include <cs50.h>
#include <stdio.h>
#include <stdlib.h> //covert str into int atoi()
#include <ctype.h> // isalpha(), isdigit() etc.
#include <string.h> //strlen() etc.
int main(int argc, string argv[])
{
// convert argv[1] into int
int key = atoi(argv[1]);
// if input is 2 command lines and argv > 0 and there is no alphabet n shit in argv[1] we good to go
if (argc == 2 && key > 0 && isdigit(argv[1]))
{
//ask for input to chiper
string plain = get_string("Plaintext: ");
int len_plain = strlen(plain);
// check each char in string plain
for (int i = 0; i < len_plain; i++)
{
// if its from 'a' to 'z' add number of key
if (plain[i] >= 'a' && plain[i] <= 'z')
{
char cipher = (plain[i] + key) % 26;
printf("%c", cipher);
return 0;
}
}
}
else
{
printf("Usage: ./caesar kay");
return 1;
}
}
When running this code, I'm faced with a segmentation fault. What did I do wrong? I have been working a few days on this code but I cannot make it work.
This isdigit(argv[1]) is giving a seg fault. The function signature of isdigit (from the man page):
int isdigit(int c);
But, argv[1] (if it exists!) is a string. Suggest you follow the spec by
verifying the right number of command line arguments is
entered
checking that each char in said argument is a decimal digit
Only then are you assured that atoi will give the desired result.
Related
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{ // see if it is correct input if not then reset and print the right way
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
for (int i = 0; i < strlen(argv[1]); i++)
{
if (!isdigit(argv[1]))
{
printf("Usage: ./caesar key\n");
return 1;
}
// cipher the text
int k = atoi(argv[1]);
string plaintext = get_string("Plain text: ");
printf("Cipher text: ");
// printing out the ciphered text
for (int j = 0; j < strlen(plaintext); j++)
if (isupper(plaintext[j]))
{
printf("%c", (plaintext[j] - 65 + k) % 26 + 65);
}
else if (islower(plaintext[j]))
{
printf("%c", (plaintext[j] - 97 + k) % 26 + 97);
}
else
{
printf("%c", plaintext[j]);
}
}
printf("\n");
}
the problem is that when I run it as it should be it only says segmentation fault. I think that the problem is at the top but im not sure
Most of your problem is simply trying to deal with the logic needed to handle that argument Cipher key. Remember, it is an input like any other, so it should be treated first, separately.
Also, avoid aliases for standard objects. It does absolutely nothing to improve readability. (It does the opposite, in fact. Any C programmer will first ask: “what is different about a string and a char *?” And then spend time only to figure out that there isn’t any. So, don’t do that.)
Finally, avoid magic numbers when dealing with character strings. Just use the character literal directly. It makes life so much easier when reading the code.
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char * argv[]) // DON’T use aliases like “string” for “char *”
{
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
int k = atoi(argv[1]); // try to turn the argument string into the Caesar offset value
if (k == 0) // fail if non-numeric input (or if input is actually “0”)
{
printf("Invalid key\n");
return 1;
}
const char * plaintext = get_string("Plain text: ");
// Convert the plaintext to the ciphertext
printf("Cipher text: ");
for (int j = 0; j < strlen(plaintext); j++)
{ // <-- don’t forget compound statement braces
if (isupper(plaintext[j]))
{
printf("%c", (plaintext[j] - 'A' + k) % 26 + 'A'); // prefer 'A' instead of 65
}
else if (islower(plaintext[j]))
{
printf("%c", (plaintext[j] - 'a' + k) % 26 + 'a');
}
else
{
printf("%c", plaintext[j]);
}
}
printf("\n");
return 0;
}
Apart from those minor problems, you did a pretty good job! Keep it up!
EDIT
Oh yeah, almost forgot. Did you need to free(plaintext) before main() terminates?
Okay, so I am completely stumped. I cannot understand why this programs output acts as if there is a random key everytime.
This program:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
string sKey = argv[1];
// Make sure program was run with just one command-line argument
if (argc != 2 || atoi(argv[1]) < 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
//Counts length of string and checks if all chars are digits
int counter = 0;
for (int i = 0; i < strlen(sKey); i++)
{
if isdigit(sKey[i])
{
counter++;
}
}
//Checks if the key is a number
if (counter != strlen(sKey))
{
printf("Usage: ./caesar key\n");
return 1;
}
// Convert argv[1] from a `string` to an `int`
int key = (int)sKey;
// Prompt user for plaintext
string plaintext = get_string("Plaintext: ");
printf("Ciphertext: ");
for (int i = 0; i < strlen(plaintext); i++)
{
if (isalpha(plaintext[i]) && isupper(plaintext[i]))
{
printf("%c", (((plaintext[i] - 'A') + key) % 26) + 'A');
}
else if (isalpha(plaintext[i]) && islower(plaintext[i]))
{
printf("%c", (((plaintext[i] - 'a') + key) % 26) + 'a');
}
else
{
printf("%c", plaintext[i]);
}
}
printf("\n");
}
Will output this:
caesar/ $ ./caesar 1
Plaintext: Hello, I'm Justin.
Ciphertext: Fcjjm, G'k Hsqrgl.
caesar/ $ ./caesar 1
Plaintext: Hello, I'm Justin.
Ciphertext: Pmttw, Q'u Rcabqv.
It seems to be due to the modulo operator, because when I isolated it I could recreate the issue. Is it one of my included libraries? I solved the problem on my own and ended up looking up a solution on youtube only to find my solution performed the same operations as the correct solution. I must be missing something.
Thank you
This is because int key = (int)sKey; does NOT convert the string to an integer... at least not in the way you think it does. It takes the string pointer sKey (a memory address) to an integer. Since every time you run the progra this can be a different address, this is why it looks random. The correct way to convert a numerical string to a value is using atoi or strtol. The first part of your program should be something like this:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
string sKey = argv[1];
int i;
// Make sure program was run with just one command-line argument
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
// Checks if all chars are digits
for (int i = 0; sKey[i]; i++)
if (!isdigit(sKey[i]) break;
// If the key contains any non-digits, error
if (sKey[i])
{
printf("Usage: ./caesar key\n");
return 1;
}
// Convert argv[1] from a `string` to an `int`
int key = atoi(sKey);
// The rest should be fine
For the CS50x Problem Caesar I have created a program that encrypts messages using Caesar’s cipher.
For this, the program must recognize that a command-line argument is only a number. So no two or more numbers, no number below zero, and no text.
But as soon as I add the check if it is a text with || isalpha(argv[1]), the program does not work anymore.
The terminal prints the following when I try to run the program:
Segmentation fault
Can anyone tell me what is the problem with the code
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int main(int argc, string argv[])
{
int kkey = 0;
// Check if correct command-line arguments
if (argc != 2 || atoi(argv[1]) < 0 || isalpha(argv[1])) //segfault here
{
printf("./caesar key\n");
return 1;
}
else
{
kkey = atoi(argv[1]);
}
// Ask for Plaintext to encrypt
string plaintext = get_string("plaintext: ");
for (int i = 0, n = strlen(plaintext); i < n; i++)
{
if (isalpha(plaintext[i]) && islower(plaintext[i]))
{
plaintext[i] = (plaintext[i] - 'a' + kkey) % 26 + 97;
}
else if (isalpha(plaintext[i]) && isupper(plaintext[i]))
{
plaintext[i] = (plaintext[i] - 'A' + kkey) % 26 + 65;
}
printf("%c", plaintext[i]);
}
printf("\n");
return 0;
}
Thank you very much for your help.
As said by #Gerhardh, you can't use strings as argument of isalpha, you need a loop to check each character of the string.
In any case that is not the best approach, using a negated isdigit would be a better option, because it accounts for all the other non numeric characters.
//...
// Check if correct command-line arguments
if (argc != 2 || atoi(argv[1]) < 0)
{
printf("./caesar key\n");
return 1;
}
for(size_t i = 0; i < strlen(argv[1]); i++){
if(!isdigit(argv[1][i])){ //if one of the characters is not a digit 0-9
puts("./caesar key\n");
return 1;
}
}
kkey = atoi(argv[1]); //no else needed
//...
Note that atoi will invoke undefined behavior if the converted value is not representable by an int.
You can use strtol for a more robust alternative.
The link is for Linux man page which I find quite nice, but this is cross-platform.
Again, as stated by #Gerhardh, using character codes may backfire, in this case you are using ASCII encoding, but there are others, this makes your code less portable, use the character instead, 26 + 'a' and 26 + 'A'.
I have another question about my caesar code-enchipering programs. Here I specified, that I want to take only integer. But somehow it proceed when 2x is inputted in the command line arguments.
Below is the full code... I deeply appreciate it if anyone can help me answer my question.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
//one command line argument with the type int
int main(int argc, string argv[])
{
//main program
if (argc == 2 && isdigit(*argv[1]))
{
int k = atoi(argv[1]); //get Caesar key into a variable
string pltext = get_string("plaintext: "); //getting input for the plain text
char cptext[strlen(pltext) + 1];
for (int i = 0, n = strlen(pltext) ; i < n; i++) //turning pltext to integer
{
if (pltext[i] >= 'a' && pltext[i] <= 'z')
{
cptext[i] = ((pltext[i] - 'a' + k) % 26) + 'a'; //shifting the integer with k (lowercase)
}
else if (pltext[i] >= 'A' && pltext[i] <= 'Z')
{
cptext[i] = ((pltext[i] - 'A' + k) % 26) + 'A'; //shifting the integer with k (uppercase)
}
else
{
cptext[i] = pltext[i]; //other symbol stays
}
}
//print out result
printf("ciphertext: %s\n", cptext);
return 0;
}
//setting condition that : K = + ; if more or less than one, immediate error message, return 1
//if not decimal return = usage ./caesar. key, return value 1 to main
else if (argc != 2)
{
printf("Error 404 : \n");
return 1;
}
else
{
printf("Usage: ./caesar key\n");
return 2;
}
}
The isdigit function only checks the first digit. meaning if I use 2x, itll see 2. what you could do is put a loop, checking the digits in the key given by the user, and see if each one is a digit!
argv[ ] in your program is char * (acc to cs50 header), so first element is a pointer to array of char's , in C programming language if you de-reference a pointer to array you get the first element in your array.
so in your program when you de-reference pointer to string "2x" ex : *argv[1], you get first element of that string (array of chars) which is 2 ! so this returning true (or 1 in this case ) and continuing that conditional statement :)
char * string = "2x";
if (string[0] == *string) print("this is the bug in your program!");
I'm trying to create a program that accepts cmd line arguments to encipher a plaintext!
The program must accept one cmd line argument after its name when making it and this would be the key which by the plaintext (only) alphabetical characters are rotated by this key (e.g. it's number is added to the real alphabet ASCII number resulting in another alphabet to be printed!
it is supposed to print an error message when one argument is present (e.g. here:/make encipher)
instead of here:/make encipher 12 <-- 12 = key!
I am getting a segmentation fault when running the program without the key argument, why?
This is the full code. I'm posting it because I need to learn where is my fault's exact location
and why is it triggered?!
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h> // To use atoi (converting a string to an int)
#include <ctype.h>
#include <string.h>
bool key_is_numb(string argv[]);
void encipher(string txt, int key);
int main(int argc, string argv[])
{
if (key_is_numb(argv) == false)
{
printf("Usage: ./caesar key\n");
return 1;
}
else
{
int key = atoi(argv[1]);
string plaintext;
if (argc == 2 && key > 0)
{
plaintext = get_string("plaintext: ");
encipher(plaintext, key); // A function that prints the ciphered text
return 0; // returns Zero as main return value which means "All good"!
}
else if (argc == 1 || argc > 2 || key <= 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
} // End else.
} // End main()å func.
bool key_is_numb(string argv[])
{
int n = strlen(argv[1]);
for (int i = 0; i < n; i++) // checking element by element in the second string of the argv[] array of strings
{
if (isdigit(argv[1][i]) == 0) // if the entered string "key" contains chars other than digits.
{
return false; // break out of the if statement & the entire function key_is_numb()
// and return false as soon as a letter is encountered.
}
else
{
continue; // go up & start the next iteration for the for loop.
}
// if only digits encountered then this for loop will come to an end and exist from here.
} // End for loop
return true; // function exits and return boolean true from here.
} // End key_is_numb() func.
void encipher(string txt, int key)
{
printf("ciphertext: ");
for (int i = 0, n = strlen(txt); i <= n; i++) // strlen counts the number of elements in a string excluding '\0'
{
char c = txt[i];
if (isalpha(c))
{
if (isupper(c))
{
char m = 'A'; // This is a modifyer character equals to 'A' = 65 so that it is indexed # ZERO!
printf("%c", (c - m + key) % 26 + m );
//c = ((((int)txt[i] - 65) + key) % 26) + 65; // char c = 65 <-- 65 is an ASCII code equals 'A'
}
else if (islower(c))
{
char m = 'a'; // This is a modifying character 'a' = 97
printf("%c", (c - m + key) % 26 + m );
}
}// End if(alpha).
else
{
printf("%c", c);
}
} // End for().
printf("\n");
} // End encipher() func.
int n = strlen(argv[1]);
in key_is_numb() and
int key = atoi(argv[1]);
in main().
If you didn't enter a key argument, argv[1] as equal as argv[argc] is a null pointer as stated in C17, §5.1.2.2.1/2.
Any attempt to access its data is undefined behavior and probably caused the segmentation fault.
Well you are assuming that argv[1] is defined in key_is_numb. However, in C and C++, the second parameter of the main function contains command line arguments. Which, in your case will be the name of the binary as the first element, then any other arguments. This is why when you are running the program without arguments, it will segfault, as there are no argument to put in argv, and no default value either.
You should always check the size of argv, by using the number stored in argc, before trying to read anything in argv.
Your segmentation fault comes from this line int n = strlen(argv[1]);, but I'd highly suggest you to learn to use debugger software like valgrind, which if the program has been compiled with debug flag will tell you the exact line.
Other debugger are really useful too, so you should learn to use them, as they usually report this kind of errors.
Your code asumes there is always an argv[1]. You should check argc which tells the number of arguments. For example:
int main(int argc, string argv[])
{
if (argc < 2) {
printf("Key required\n");
exit (1);
}