I need to check if there is first a key entered in the comand line (which i have done and works). I then need to check that the key entered is not alphabetical.
I have managed to make it so that if a aplha key was entered it will display the warning, however if the key is entered for example as 2THG. It still works using the number as opposed to showing the warning.
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main (int argc , string argv[])
{
if (argc != 2 )
{
printf ("Usage: ./caesar key\n");
return 1;
}
if (isalpha (*argv [1]))
{
printf ("usage: ./caesar key\n");
return 1;
}
call to "isalpha (*argv [1])" checks only the first char. If we need to check te string each char of the string needs to be passed to isalpha separately.
Sample code:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main (int argc , char* argv[]) {
int i = 0;
if (argc != 2 ) {
printf ("Usage: ./caesar key -1\n");
return 1;
}
for (i = 0; i < strlen(argv[1]); i++) {
if (isalpha(argv[1][i])) {
printf ("usage: ./caesar key\n");
return 1;
}
}
return 0;
}
This expression:
*argv [1]
Checks only the first character of the array argv[1]. To test all characters, loop thorough using one of the following equivalent expressions:
argv[1][i]
or:
*(argv[1] + i)
Also, there is a similar function used to test for numeric character values: int isdigit(int c). It is illustrated below in a loop to verify content of argv[1]:
int len = strlen(argv[1]);
for(int i=0;i<len;i++)
{
if(!isdigit(argv[1][i]))//leave upon first non-numeric character
{
printf ("usage: ./caesar key\n");
return 1;
}
}
Related
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;
}
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;
}
I am looking to write a code where if a user enters a non digit command line argument, a message of "Usage: ./caesar key\n" pops up.
Currently I am able to only accept one command line argument which is good. Problem is when the user types multiple characters the printf ("Success\n"); and printf ("%s\n", argv[1]); keeps repeating.
I also wanted that if the user enters for example: "12x" they get an error message of "Usage: ./caesar key" instead of looping through and giving me "success", "12x", "success", "12x" and "usage: ./caesar key".
For example:
~/workspace/pset2/caesar/ $ ./caesar 12x =>
Success
12x
Success
12x
Usage: ./caesar key
I want it to only be "Usage: ./caesar key" instead of all of them being printed out when there is even only a single non digit.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdbool.h>
int main(int argc, string argv [])
{
if (argc == 2)
{
int n = strlen(argv[1]);
for (int i = 0; i < n; i++)
{
if (isdigit(argv[1][i]))
{
printf ("Success\n");
printf ("%s\n", argv[1]);
}
else
{
printf ("Usage: ./caesar key\n");
}
}
}
else
{
printf ("Usage: ./caesar key\n");
}
}
Here's the way I use to handle these situations:
declare a flag variable initialized to 1 or true if you use bool type
use the loop to check an unwanted condition and if it is satisfied you set flag to 0 or false and break the loop
after the loop use the condition if( flag ) to do something in case it was all right
int flag = 1;
for (int i = 0; i < n; i++) {
if( !(isdigit(argv[1][i])) ) {
printf ("Usage: ./caesar key\n");
flag = 0;
break;
}
}
if( flag ) {
printf ("Success\n");
printf ("%s\n", argv[1]);
}
You can of course name the flag as you prefer. int onlyDigits = 1 with if( onlyDigits ) would result in a more readable code.
Your code was only working for one command line argument, given that the total arguments were two. To answer your question, you are also printing Success every time you find a character that is a digit, what you might want to do is check, if you want to go with that approach, if each character is not a digit, then you can terminate. You can also use strspn(), and check for a numbers in your string, like so:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void usage(void)
{
fprintf(stderr,"Usage: ...\n");
return;
}
int main(int argc, char **argv)
{
int i = 1; /* Ignoring executable name */
char *digits = "0123456789";
if (argc >= 2)
{
for (; i < argc; i++)
{
if ((strlen(argv[i]) != strspn(argv[i], digits))) /* Checking if the length of the string is not equal to the matched numbers */
{
fprintf(stderr, "Invalid argument %s\n", argv[i]);
usage();
exit(EXIT_FAILURE);
}
}
}
else
{
fprintf(stderr, "Invalid number of arguments\n");
usage();
exit(EXIT_FAILURE);
}
return 0;
}
Basically, you check if the length of your string is not equal to the length of the substringwhich can contain any of the digits in string digits, if it is equal, you know that the string is actually made of only digits. More on strspn() here .
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++;
}
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.