Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcing String to int Function to Consume Entire String

Given a string that should represent a number, I'd like to put it into a conversion function which would provide notification if the whole string did not convert.

For input: "12":

  • istringstream::operator>> outputs 12
  • atoi outputs 12
  • stoi outputs 12

For input "1X" I'd like a failure response but I get:

  • istringstream::operator>> outputs 1
  • atoi outputs 1
  • stoi outputs 1

For input "X2":

  • istringstream::operator>> outputs 0 and sets an error flag
  • atoi outputs 0
  • stoi throws an error

[Live Example]

Is there a way to provoke the error behavior on input "1X"?

like image 883
Jonathan Mee Avatar asked Oct 07 '15 11:10

Jonathan Mee


People also ask

What function converts strings to integers?

The atoi() function converts a character string to an integer value. The input string is a sequence of characters that can be interpreted as a numeric value of the specified return type.

How do I convert a string to an int in C++?

Using the stoi() function The stoi() function converts a string data to an integer type by passing the string as a parameter to return an integer value. The stoi() function contains an str argument. The str string is passed inside the stoi() function to convert string data into an integer value.

How does stoi work in C++?

In C++, the stoi() function converts a string to an integer value. The function is shorthand for “string to integer,” and C++ programmers use it to parse integers out of strings. The stoi() function is relatively new, as it was only added to the language as of its latest revision (C++11) in 2011.

Does atoi ignore whitespace?

stoistolstoll Interprets a signed integer value in the string str . Function discards any whitespace characters (as identified by calling isspace() ) until first non-whitespace character is found.


1 Answers

Edit: In c++17 or later from_chars is preferred. See here for more: https://topanswers.xyz/cplusplus?q=724#a839


For a given string str there are several ways to accomplish this each with advantages and disadvantages. I've written a live example here: https://ideone.com/LO2Qnq and discuss each below:

strtol

As suggested here strtol's out-parameter can be used to get the number of characters read. strtol actually returns a long not an int so a cast is happening on the return.

char* size;
const int num = strtol(str.c_str(), &size, 10);

if(distance(str.c_str(), const_cast<const char*>(size)) == str.size()) {
    cout << "strtol: " << num << endl;
} else {
    cout << "strtol: error\n";
}

Note that this uses str.c_str() to refer to the same string. c_str Returns pointer to the underlying array serving as character storage not a temporary if you have C++11:

c_str() and data() perform the same function

Also note that the pointer returned by c_str will be valid between the strtol and distance calls unless:

  • Passing a non-const reference to the string to any standard library function
  • Calling non-const member functions on the string, excluding operator[], at(), front(), back(), begin(), rbegin(), end() and rend()

If you violate either of these cases you'll need to make a temporary copy of i's underlying const char* and perform the test on that.

sscanf

sscanf can use %zn to return the number of characters read which may be more intuitive than doing a pointer comparison. If base is important, sscanf may not be a good choice. Unlike strtol and stoi which support bases 2 - 36, sscanf provides specifiers for only octal (%o), decimal (%d), and hexadecimal (%x).

size_t size;
int num;

if(sscanf(str.c_str(), "%d%zn", &num, &size) == 1 && size == str.size()) {
    cout << "sscanf: " << num << endl;
} else {
    cout << "sscanf: error\n";
}

stoi

As suggested here stoi's output parameter works like sscanf's %n returning the number of characters read. In keeping with C++ this takes a string and unlike the C implementations above stoi throws an invalid_argument if the first non-whitespace character is not considered a digit for the current base, and this unfortunately means that unlike the C implementations this must check for an error in both the try and catch blocks.

try {
    size_t size;
    const auto num = stoi(str, &size);

    if(size == str.size()) {
        cout << "stoi: " << num << endl;
    } else {
        throw invalid_argument("invalid stoi argument");
    }
} catch(const invalid_argument& /*e*/) {
    cout << "stoi: error\n";
}
like image 100
Jonathan Mee Avatar answered Oct 22 '22 01:10

Jonathan Mee