Problem with the Caesar problem set on cs50 online course - c

I'm on the stage where we need to correctly setup the command line argument to take only digits and to break out of the code if the user types anything else other than a digit. (leaving it empty, inputting two digits, inputting text, inputting two pieces of text etc.)
So I made the program work correctly if the user types more than one command line argument or one command line argument which isn't a digit. My problem is that if I leave the command line argument blank the program gives me a segmentation fault and it doesn't work as intended if I type a single command line like "1d". Can anyone tell me where I went wrong? I'm stuck. Here's the code:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
bool only_digits(string argv[]);
int main(int argc, string argv[])
{
bool digits = only_digits(argv);
if(argc != 2 || digits == false)
{
printf("Usage: ./caesar key\n");
return 1;
}
else
{
return 0;
}
}
bool only_digits(string argv[])
{
bool digit = false;
for(int i = 0; i < strlen(argv[1]); i++)
{
if(isdigit(argv[1][i]))
{
digit = true;
}
}
return digit;
}

You have two problems:
You're calling only_digits() before checking whether argv[1] exists. So you get a segfault if there are no command-line arguments. Fix this by calling only_digits() after the argc check.
The condition in only_digits() is backwards. It returns true if any character is a digit, but it's supposed to return true only if all the characters are digits.
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
bool only_digits(string argv[]);
int main(int argc, string argv[])
{
if(!(argc == 2 && only_digits(argv)))
{
printf("Usage: ./caesar key\n");
return 1;
}
return 0;
}
bool only_digits(string argv[])
{
for(int i = 0; i < strlen(argv[1]); i++)
{
if(!isdigit(argv[1][i]))
{
return false;
}
}
return true;
}

Related

Command line argv not showing up during debugging (using github codespace)

when I input debug50 Caeser 1024 into my code space argc shows as 2 which is correct but argv shows as 0x7ffd87a68798
as shown here and idk why it doesn't show as 1024?
this is the first time I've used command line arguments so would appreciate any help, please.
This is my code so far also would appreciate any checks on my function I think that's wrong as well :/
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
bool only_digit(string argv[1]);
int main(int argc, string argv[])
{
//Make sure program was run with command line argument (argc)
if (argc == 2){
return 0;
}
else{
printf("Usage: ./caesar key\n");
return 1;
}
}
//Make sure every character in argv[1] is a number
bool only_digit(string argv[1]){
if (isdigit((*argv[1]))){
return true;
}
else{
return false;
}
}
You should check if no. of arguments doesn't match what you expect first and return if it doesn't.
Also, you should learn how to declare/define functions properly with arguments. argv[1] means you are expecting an array of strings of size 1.
Also, your function can be a single return statement.
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
bool only_digit(string s);
int main(int argc, string argv[]) {
// Make sure program was run with command line argument (argc)
if (argc != 2) {
printf("Usage: ./caesar key\n");
return 1;
}
printf("%d\n", only_digit(argv[1]));
}
// Make sure every character in argv[1] is a number
bool only_digit(string s) {
return isdigit(*s);
}

Segmentation Fault Due to Lack of Command Line Argument (argv[1])

This segment of code is meant to check if a user has entered only one numeric command-line argument, and return an error code of "1" if this is not the case. I have the code set up so that it first checks if argc is anything other than 2. Unfortunately, I am still receiving Segmentation Faults if no command line argument is entered, and I'm not sure why this code doesn't catch a null amount of command line arguments.
I tried moving the "if (argc !=2)" formula above the entire "for" statement to try and catch the command line argument issue right from the beginning, but I received the same result.
My question is, why am I receiving a Segmentation Fault when no command line argument is provided, and what am I missing to ensure the program doesn't Seg Fault with no command line argument?
Due to course policy, I will only be providing the segment of code in question.
#include <unistd.h>
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
// add'l variables //
int k = atoi(argv[1]);
for (int i = 0; i < strlen(argv[1]); i++)
{
if (argc != 2)
{
printf("Please enter only 1 command-line argument.\n");
return 1;
}
else if (!isdigit(argv[1][i]))
{
printf("Usage: ./caesar key\n");
return 1;
}
}
// add'l code //
Error as shown in Terminal
You must first check the argc before using the argv[1], because argv[1] may not have a valid pointer if argc < 2. A corrected version of your code could be like that:
#include <stdio.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
int i = 0;
if (argc != 2) {
printf("Please enter only 1 command-line argument.\n");
return 1;
}
while (isdigit(argv[1][i]))
++i;
if (argv[1][i] != '\0') {
printf("Usage: ./caesar key\n");
return 1;
}
return 0;
}

Check if first argument is a valid number consisting of only decimal digits [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
This is the code I'm trying to use for the "validating the key" part to try to return success only when the argument is a digit. However, when I run my program, some numbers are considered wrong. I don't understand why.
~/pset2/caesar/ $ ./caesar 9
Usage: ./caesar key
~/pset2/caesar/ $ ./caesar 45
Success
45
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[]) {
int n = atoi(argv[1]);
if (argc == 2 && isdigit(argv[1][n])) {
printf("Success\n%s\n", argv[1]);
return 0;
} else {
printf("Usage: ./caesar key\n");
return 1;
}
Your error is here:
if (argc == 2 && isdigit(argv[1][n])) {
This line does not make sense, you are checking if the n-th character of the first argument is a digit, but (1) you don't even know if the first argument has enough characters (n is returned by atoi so it could be arbitrarily large) and (2) you're not checking all digits of the argument.
If you want to check that every single character in the first argument given to your program is a digit you can do so in two ways:
Iterate over each character and check with isdigit():
#include <stdio.h>
#include <ctype.h>
int main(int argc, char **argv) {
char *c;
if (argc != 2) {
fputs("Usage: ./prog <key>\n", stderr);
return 1;
}
for (c = argv[1]; *c != '\0'; c++) {
if (!isdigit(*c)) {
fputs("Usage: ./prog <key>\n", stderr);
return 1;
}
}
printf("Success!\n%s\n", argv[1]);
return 0;
}
Use a function like strtol (not atoi, since it does not signal errors). Using strtol also has the benefit of automatically checking for you if the number is in range of the values that can be stored in a long (if not, LONG_MIN or LONG_MAX is returned and errno is set appropriately).
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
int main(int argc, char **argv) {
long num;
char *endp;
if (argc != 2) {
fputs("Usage: ./prog <key>\n", stderr);
return 1;
}
errno = 0;
num = strtol(argv[1], &endp, 10);
if (endp == argv[1] || *endp != '\0' || errno == ERANGE) {
fputs("Usage: ./prog <key>\n", stderr);
return 1;
}
printf("Success!\n%ld\n", num);
return 0;
}
Option 2 has the advantage of already converting the number for you, so I would suggest that over option 1. NOTE THAT strtol can return a negative value if the given string starts with - (e.g. -123): you might want to check for that if you don't allow negative numbers.
A proper way to validate such inputs is to use strspn and check it ate the whole string. In almost all cases strspn will be faster than any hand-coded version, unless maybe if you combine validation and conversion.
Here's your code with a validation function doing exactly that:
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int validate_arg(const char *s) {
size_t len;
len = strspn(s, "0123456789");
return len > 0 && !s[len];
}
int main(int argc, string argv[]) {
int n = atoi(argv[1]);
if (argc == 2 && validate_arg(argv[1])) {
printf("Success\n%s\n", argv[1]);
return 0;
} else {
printf("Usage: ./caesar key\n");
return 1;
}
}
Side-note: It would be better to use an unsigned type and convert the number using strtoul.
when I run my program, some numbers are considered wrong.
With an input argument like "123", the below code attempts to check if the 123rd character of the 3 character argument is a digit. Access of argv[1] outside "123" is undefined behavior (UB) - bad.
int n = atoi(argv[1]);
if (... isdigit(argv[1][n])) ... // UB
To test if input is all digits:
Check for the expected number of arguments before using argv[1].
Test each character of string argv[1][].
Sample
int main(int argc, string argv[]) {
if (argc == 2) {
const char *digit = argv[1];
while (*digit >= '0' && *digit <= '9') {
digit++;
}
// If code reached the end of the string?
if (*digit == '\0') {
printf("Success\n%s\n", argv[1]);
return 0;
}
}
printf("Usage: ./caesar key\n");
return 1;
}
Code could alternatively use isdigit() which is best called with unsigned char values:
const unsigned char *digit = argv[1];
while (isdigit(*digit)) {
digit++;
}

Cannot get my program to print "Usage: ./caesar"

I am currently doing cs50 course, I am doing the caesar cipher problem and have come across a problem.
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
// argument count, array of strings
int main (int argc, string argv[])
{
//checking there is only one command line argument, checking if digit
if (argc == 2 && isdigit(*argv[1]))
{ //atoi converts string to int
int k = atoi(argv[1]);
//get plaintext
string s = get_string ("Plain text: ");
printf("ciphertext: \n");
if (argc !=2)
printf("Usage: ./ceasar\n");
}
}
I cannot get my program to print "Usage: ./caesar"
If I enter ./cipher 2 it returns
Plaintext:
but if I enter say ./caesar g it returns to a blank line in terminal.
Any help would be appreciated.
You miss an else branch:
if (argc == 2 && isdigit(*argv[1]))
{ //atoi converts string to int
int k = atoi(argv[1]);
//get plaintext
string s = get_string ("Plain text: ");
printf("ciphertext: \n");
... // the rest of your code
}
else
{
printf("Usage: ./ceasar\n"); // You mean Usage: ./ceasar param
}
It's a better practice to check arguments first and exit earlier. Here is a link to run the code. You might need it later when you encounter segfaults.
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// argument count, array of strings
int main(int argc, string argv[]) {
if (argc != 2) {
printf("Usage: ./ceasar\n");
exit (1);
}
// checking there is only one command line argument, checking if digit
if (argc == 2 && isdigit(*argv[1])) { // atoi converts string to int
int k = atoi(argv[1]);
// get plaintext
string s = get_string("Plain text: ");
printf("ciphertext: \n");
}
return 0;
}
Your if condition has no else branch,
if (argc == 2 && isdigit(*argv[1]))
checks if the input is a digit, if g is entered, the condition fails and the code in the if block is not executed, and the program exits with exit code 0. Use:
if (argc == 2 && isdigit(*argv[1]))
{
//Your code
}
else
{
//Else code if your condition fails
}

What do I need to change so cs50 Caesar only prints the correct message after i iterate over each character?

I am stuck trying to iterate over each character to detect whether or not it is a digit, while only printing the answer once. The problem I'm having is I can get it to detect whether a character is a digit or not, but it prints out an answer for each number I put in until it reaches a letter. I'm looking how to get it to detect whether or not the input is a number or letter, and then make a decision on what to print out instead of printing success every time it detects a number. Feel like its something with my for loop but cant quite figure it out. Thanks.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
string n = argv[1];
if (argc != 2)
{
printf("usage: ./caesar key\n");
return 1;
}
else
{
for(int i = 0, length = strlen(n); i < length; i++)
if(!isdigit(n[i]))
{
printf("usage: ./caesar key\n");
return 1;
}
else
{
int convert = atoi(n);
printf("Success\n");
printf("%i\n", convert);
}
}
}
The problem is that inside the for you have an if-else statement... that means that one of both will always execute for every iterarion.
For printing success just once, you should erase the else keyword (and for clarity the brackets) but keeping the else code.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
string n = argv[1];
if (argc != 2)
{
printf("usage: ./caesar key\n");
return 1;
}
else
{
for(int i = 0, length = strlen(n); i < length; i++)
{
if(!isdigit(n[i]))
{
printf("usage: ./caesar key\n");
return 1;
}
}
int convert = atoi(n);
printf("Success\n");
printf("%i\n", convert);
}
}
What do I need to change so cs50 Caesar only prints the correct message after i iterate over each character?
It seems you aren't aware that you don't have to do that. The Caesar Specification says:
You can assume that, if a user does provide a command-line argument, it will be a non-negative integer (e.g., 1). No need to check that it’s indeed numeric.

Resources