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;
}
tshepang
  • 12,111
  • 21
  • 91
  • 136
Ryan Mann
  • 53
  • 1
  • 10
  • Is there something wrong with your current implementation? If so, tell us how your implementation differs from your requirements. – Oswald Apr 22 '13 at 21:18
  • 4
    `atoi` isn't sophisticated enough to give the right answer here. Look at `strtol`. – Pete Becker Apr 22 '13 at 21:19
  • 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. – Ryan Mann Apr 22 '13 at 21:20
  • 1
    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. – Keith Thompson Apr 22 '13 at 21:22
  • i just didnt include them all, argc is already checked and good and all of the #includes are in the .h file – Ryan Mann Apr 22 '13 at 21:23

5 Answers5

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.

hmjd
  • 120,187
  • 20
  • 207
  • 252
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.

hookenz
  • 36,432
  • 45
  • 177
  • 286
  • Without a format specifier `%` on the `"u"`, this isn't libel to to much. – WhozCraig Apr 22 '13 at 21:42
  • 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.)" – Alan Shutko Apr 22 '13 at 21:53
  • err true, I haven't used strtol for a very long time. Fixed the missing % - oops. – hookenz Apr 22 '13 at 22:39
  • 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` = 4294954729 – Mike Kinghan Apr 22 '13 at 23:09
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.

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
  • +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`. – Steve Jessop Apr 22 '13 at 23:40
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 );
}
Arun
  • 19,750
  • 10
  • 51
  • 60
-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.

Charles D Pantoga
  • 4,307
  • 1
  • 15
  • 14
  • isdigit works at the character not char array level. So isdigit(argv[1]) doesn't do what you think if it even compiles. – hookenz Apr 23 '13 at 03:48