Checking to make sure argv[1] is an integer c++
Asked Answered
S

5

2

For my program I have to make sure the user only inputs a positive INTEGER. for example if the user inputted 12hi it should not run the program and print to std error. I am not quite sure how to implement this.

int main(int argc, char *argv[])   
{ 
    if(atoi(argv[1]) < 1)
    {
        cerr << "ERROR!"<< endl;
        return 1;
    }
    return 0;
}
Salvatore answered 22/4, 2013 at 21:17 Comment(5)
Is there something wrong with your current implementation? If so, tell us how your implementation differs from your requirements.Dekaliter
atoi isn't sophisticated enough to give the right answer here. Look at strtol.Madaras
This only checks if it is a positive number so far, i cant find a way to check if it is an integer, for example: if you recieved 28 from argv, main should return 0 but if you recieved 28abc, main should return false.Salvatore
You need to check the value of argc before you even think about examining argv[1]. Consider what happens if you run your program with no arguments. And you're missing at least a couple of required #include directives.Babbling
i just didnt include them all, argc is already checked and good and all of the #includes are in the .h fileSalvatore
H
6

Pass it to a std::istringstream and ensure all data was processed:

if (a_argc > 1)
{
    std::istringstream in(a_argv[1]);
    int i;
    if (in >> i && in.eof())
    {
        std::cout << "Valid integer\n";
    }
}

See online demo at http://ideone.com/8bEYJq.

Hernandez answered 22/4, 2013 at 21:35 Comment(1)
This is exactly what I was looking for!Homothermal
N
1

Ok, my revised answer. sscanf wasn't behaving how I thought it would and strtol provides the best C-like solution that is very portable.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
  for (int i=1; i < argc; i++){
      char* end;
      long val = strtol(argv[i], &end, 10);
      if (argc >= 2 && !end[0] && val >= 0){
          printf("%s is valid\n", argv[i]);
      } else {
          printf("%s is invalid\n", argv[i]);
      }
  }
  return 0;
}

Sample output: ./a.out 10 -1 32 1000 f -12347 +4 --10 10rubbish

10 is valid
-1 is valid
32 is valid
1000 is valid
f is invalid
-12347 is valid
+4 is invalid
--10 is invalid
10rubbish is invalid

This works because strtol will convert the argument to a long int. Then if end[0] is not at the end of the string it will be non-zero meaning it'll throw up an error for 10rubbish but be ok for values like 10. Then of course we only want positive integers and I've included the value 0 in that set.

atoi() by itself is not good enough as it returns zero for failure. 0 could be a valid input.

sscanf() also by itself is not good enough because it'll successfully convert strings like 10rubbish and return the value 10.

I realise op only wants argv[1], this answer scans through all provided args just to show the output of lots of valid and invalid entries.

Neoteny answered 22/4, 2013 at 21:33 Comment(4)
Without a format specifier % on the "u", this isn't libel to to much.Haeres
strtol will work fine, since you pass a pointer to a pointer it will stuff with the end of the passed info. Confirm that is the end of the string. " If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr. If there were no digits at all, however, strtol() stores the original value of str in *endptr. (Thus, if *str is not \0' but **endptr is \0' on return, the entire string was valid.)"Stupid
err true, I haven't used strtol for a very long time. Fixed the missing % - oops.Neoteny
The solution does not allow the program to detect negative integer arguments, which the OP wishes to reject. A negative integer argument will be accepted and converted to unsigned, e.g. "-12567" will give val = 4294954729Yogini
Y
1

Since you evidently do not object to using the Standard C library, the function

long strtol (const char* str, char** endptr, int base)

from <cstdlib> is quite sufficient to ensure that the commandline argument is a (long) integer numeral with an optional "-" or "+" prefix, and nothing more than that. You merely need to check that the char * stored at endptr on return addresses '\0', which tells you that the function has consumed the entire argument.

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])   
{
    if (argc < 2) {
        return 1;
    }

    char * endp;
    long i = strtol(argv[1],&endp,10);
    if (!*endp) {
        cout << "The value of \"" << argv[1] << "\" is " << i << endl;
        return 0;
    }
    cerr << "\"" << argv[1] << "\" is not an integer" << endl;
    return 1;
}

LATER ...or catering for Steve Jessop's comments:

#include <cstdlib>
#include <iostream>
#include <climits>

using namespace std;

int main(int argc, char *argv[])   
{
    if (argc < 2) {
        return 1;
    }

    char * endp;
    long i = strtol(argv[1],&endp,10);

    if (*endp) {
        cerr << "\"" << argv[1] << "\" is not an integer :(" << endl;
        return 1;
    }
    if (endp == argv[1]) {
        cerr << "Empty string passed :(" << endl;
        return 1;
    }
    if (i < 0) {
        cerr << "Negative " << i << " passed :(" << endl;
        return 1;
    }
    if (i <= INT_MAX) {
        cout << "Non-negative int " << i << " passed :)" << endl;
    } else {
        cout << "Non-negative long " << i << " passed :)" << endl;
    }
    return 0;

}

A wrapper function would be in order for this degree of discrimination. And there remains the very-very corner case that an input of ULONG_MAX will be accepted as LONG_MAX.

Yogini answered 22/4, 2013 at 22:34 Comment(1)
+1, but there's one error which is that you haven't correctly handled the case where argv[1] points to an empty string. There's also a possible improvement, that you might want to print a different message in the case where the input string represents a value out of range of long.Cordwood
V
0

You can try checking if all the characters in argv[1] are digits (possibly with a leading minus sign). The check can be performed by using the standard library function isdigit().

http://www.cplusplus.com/reference/cctype/isdigit/

Complete solution based on OP's actual code (also available at http://codepad.org/SUzcfZYp):

#include <stdio.h>          // printf()
#include <stdlib.h>         // atoi()
#include <ctype.h>          // isdigit()

int main(int argc, char *argv[])   
{ 
    if( argc != 2 ) {
        return 0;
    }

    char * pWord = argv[ 1 ];
    char c = 0;
    for( int i = 0; c = pWord[ i ], c ; ++i ) {
        if( ! isdigit( c ) ) {
            return 0;
        }
    }

    int argvNum = atoi( argv[ 1 ] );
    printf( "argc = %d, argv[ 1 ] = %s, argvNum = %d\n",
        argc, argv[ 1 ], argvNum );
}
Vedda answered 22/4, 2013 at 21:24 Comment(0)
O
-1

I'm new to C++ so please don't flame me if this is wrong, but couldn't you throw an exception and allow the user to re-correct the input?

I've learned a few ways of dealing with errors:

  1. If/Else handling
  2. Assert
  3. Throw exception

1.IF/ELSE #include

int main(int argc, int **argv) {
    if (!isdigit(argv[1])) {
        // handle code if it's not a digit.
        return 0;
    }
}

This is probably the easiest way to make sure


2.ASSERT #include

int main(int argc, int *argv[]) {
    assert(isdigit(argv[1]));
}

* Assert will terminate the program if argv[1] is not a digit

3.THROW #include

using namespace std;

class Except {};

int main(int argc, int **argv) {
    try {
        isdigit(argv[1]);
        throw Except();
        // this code will not be executed
        // if argv[1] is not a digit
    }
    catch (Except) {
        cout << "argv[1] is not a digit.";
        // handle exception or rethrow
    } 
}

It is definitely worth noting that throwing an exception will create a stack trace and also all code in-between the thrown exception and the block that catches the exception will NOT be executed.

Overcareful answered 23/4, 2013 at 0:44 Comment(1)
isdigit works at the character not char array level. So isdigit(argv[1]) doesn't do what you think if it even compiles.Neoteny

© 2022 - 2024 — McMap. All rights reserved.