Search code examples
c++windowsc++11error-handlingstrtol

C++: check if string is a valid integer using "strtol"


I have heard that I should use strtol instead of atoi due to its better error handling. I wanted to test out strtol by seeing if I could use this code to check if a string is an integer:

#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
    string testString = "ANYTHING";
    cout << "testString = " << testString << endl;
    int testInt = strtol(testString.c_str(),NULL,0);
    cout << "errno = " << errno << endl;
    if (errno > 0)
    {
        cout << "There was an error." << endl;
        cout << "testInt = " << testInt << endl;
    }
    else
    {
        cout << "Success." << endl;
        cout << "testInt = " << testInt << endl;
    }
    return 0;
}

I replaced ANYTHING with 5 and it worked perfectly:

testString = 5
errno = 0
Success.
testInt = 5

And when I do it with 2147483648, the largest possible int + 1 (2147483648), it returns this:

testString = 2147483648
errno = 34
There was an error.
testInt = 2147483647

Fair enough. But, when i try it with Hello world!, it incorrectly thinks it's a valid int and returns 0:

testString = Hello world!
errno = 0
Success.
testInt = 0

Notes:

  • I am using Code::Blocks with GNU GCC Compiler on Windows
  • "Have g++ follow the C++11 ISO C++ language standard [-std=c++11]" is checked in "Compiler Flags".

Solution

  • According with the man page of strtol. You must define your function such as:

    bool isNumeric(const std::string& str) {
        char *end;
        long val = std::strtol(str.c_str(), &end, 10);
        if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) {
            //  if the converted value would fall out of the range of the result type.
            return false;   
        }
        if (end == str) {
           // No digits were found.
           return false;
        }
        // check if the string was fully processed.
        return *end == '\0';
    }
    

    In C++11, I prefer to use std::stol instead of std::strtol, such as:

    bool isNumeric(const std::string& str) {
        try {
            size_t sz;
            std::stol(str, &sz);
            return sz == str.size();
        } catch (const std::invalid_argument&) {
            // if no conversion could be performed.
            return false;   
        } catch (const std::out_of_range&) {
            //  if the converted value would fall out of the range of the result type.
            return false;
        }
    }
    

    std::stol calls std::strtol, but you works directly with std::string and the code is simplified.