Modred
Modred

Reputation: 269

Checking input is an integer

I am trying to ensure user input is a positive integer and have got my function to work no matter what the user tries to put in.

int getNumber()
{
    string userInput;
    int userNumber;
    bool badInput = true;
    cout<<"Enter a positive integer: ";
    cin>>userInput;
    while (badInput)
    {
        for (int i=0; i<userInput.length(); i++)
        {
            if (isdigit(userInput[i]))
            {
                badInput = false;
            }
            else
            {
                badInput = true;
                cout<<"That wasn't a valid input, try again: ";
                cin>>userInput;
                break;
            }
        }
    }
    userNumber = atoi(userInput.c_str());
    return userNumber;
}

Is there a cleaner way to do this or is this the best way? I tried various other methods such as using cin.bad etc but they always managed to miss some issue.

Upvotes: 0

Views: 1433

Answers (4)

James Kanze
James Kanze

Reputation: 153909

If you need to check for a positive integer, you probably need to convert the text to an integer as well. You should do both at once; something along the lines of:

std::string line;
if ( !std::getline( std::cin, line ) ) {
    //  no input available...
} else {
    std::istringstream parser( line );
    int userNumber;
    if ( parser >> userNumber >> std::ws
            && parser.get() == EOF
            && userNumber > 0 ) {
        //  All OK...
    } else {
        //  Some error in the input.
    }
}

It's also possible to use strtol, if you have the string from elsewhere. Error detection is a bit tricky, because strtol has some strange semantics in some cases:

int
getPositiveInteger( std::string const& input )
{
    char const* end;
    errno = 0;
    long results = strtol( input.c_str(), &end, 10 );
    if ( end != input.c_str() ) {
        while ( isspace( static_cast<unsigned char>( *end) ) ) {
            ++ end;
        }
    }
    return (errno == 0
            && end != input.c_str()
            && *end == '\0'
            && results > 0
            && results <= INT_MAX)
        ? results
        : -1;
}

(I've returned -1 in case of an error.)

You'll notice the number of conditions you have to test to be sure that stdtol has worked correctly.

Upvotes: 1

Olaf Dietsche
Olaf Dietsche

Reputation: 74018

You can use std::string::find_first_not_of() for testing against digits and std::stoi for overflow

std::string input;
bool badInput;
int i;

std::cout << "Enter a positive integer: ";
do {
    std::cin >> input;
    try {
        badInput = input.find_first_not_of("0123456789") != std::string::npos;
        if (!badInput)
            i = std::stoi(input);
    } catch (const std::invalid_argument&) {
        badInput = true;
    } catch (const std::out_of_range&) {
        badInput = true;
    }

    if (badInput)
        std::cout << "That wasn't a valid input, try again: ";
} while (badInput);

This doesn't check for a leading +, though.

Upvotes: 0

user238801
user238801

Reputation:

Like mvp said, strol is the way to go.

bool string_to_int(char* s, int* val) {
   char* endptr;
   errno = 0; /* reset errno */
   int i = strtol(s, &endptr, 10);
   if (endptr == s) return false; /* conversion failed */
   if (*endptr) return false; /* unexpected characters behind the integer */
   if (errno) return false; /* out of range */
   *val = i;
   return true;
}

Upvotes: 0

mvp
mvp

Reputation: 116088

You can simply use strtol, something like this:

char str[] = "+12345";
char* end;
long number = strtol(str, &end, 10);
if (number < 0) {
    printf("Number is negative!\n");
    // ...
}
if (*end) {
    printf("Junk found after number: '%s'\n", end);
    // ...
}

Upvotes: 0

Related Questions