Can I tell if a std::string represents a number using stringstream?

10,335

Solution 1

You should use an istringstream so that it knows it's trying to parse input. Also, just check the result of the extraction directly rather than using good later.

#include <sstream>
#include <iostream>

int main()
{
    std::istringstream ss("2");
    double d = 0.0;
    if(ss >> d) {std::cout<<"number"<<std::endl;}
    else {std::cout<<"other"<<std::endl;}
}

Solution 2

Don't use good()! Test if the stream is failed or not:

if (ss)

Good tells you if any of eofbit, badbit, or failbit are set, while fail() tells you about badbit and failbit. You almost never care about eofbit unless you already know the stream is failed, so you almost never want to use good.

Note that testing the stream directly, as above, is exactly equivalent to:

if (!ss.fail())

Conversely, !ss is equivalent to ss.fail().


Combining the extraction into the conditional expression:

if (ss >> d) {/*...*/}

Is exactly equivalent to:

ss >> d;
if (ss) {/*...*/}

However, you probably want to test if the complete string can be converted to a double, which is a bit more involved. Use boost::lexical_cast which already handles all of the cases.

Solution 3

If you want to check whether a string contains only a number and nothing else (except whitespace), use this:

#include <sstream>

bool is_numeric (const std::string& str) {
    std::istringstream ss(str);
    double dbl;
    ss >> dbl;      // try to read the number
    ss >> std::ws;  // eat whitespace after number

    if (!ss.fail() && ss.eof()) {
        return true;  // is-a-number
    } else {
        return false; // not-a-number
    }
}

The ss >> std::ws is important for accepting numbers with trailing whitespace such as "24 ".

The ss.eof() check is important for rejecting strings like "24 abc". It ensures that we reached the end of the string after reading the number (and whitespace).

Test harness:

#include <iostream>
#include <iomanip>

int main()
{
    std::string tests[8] = {
            "", "XYZ", "a26", "3.3a", "42 a", "764", " 132.0", "930 "
    };
    std::string is_a[2] = { "not a number", "is a number" };
    for (size_t i = 0; i < sizeof(tests)/sizeof(std::string); ++i) {
        std::cout << std::setw(8) << "'" + tests[i] + "'" << ": ";
        std::cout << is_a [is_numeric (tests[i])] << std::endl;
    }
}

Output:

      '': not a number
   'XYZ': not a number
   'a26': not a number
  '3.3a': not a number
  '42 a': not a number
   '764': is a number
' 132.0': is a number
  '930 ': is a number
Share:
10,335
alan2here
Author by

alan2here

Updated on June 04, 2022

Comments

  • alan2here
    alan2here almost 2 years

    Apparently this is suposed to work in showing if a string is numerical, for example "12.5" == yes, "abc" == no. However I get a no reguardless of the input.

    std::stringstream ss("2");
    double d; ss >> d;
    if(ss.good()) {std::cout<<"number"<<std::endl;}
    else {std::cout<<"other"<<std::endl;}
    
  • Fred Nurk
    Fred Nurk about 13 years
    How does "it knows it's trying to parse input" matter at all? Perhaps good for code clarity, but I see no technical reason.
  • Tom
    Tom about 13 years
    You're not checking for extra content. "2 abc" isn't a number, for example - it's a string with embedded whitespace. IIRC, the solution is to check {{ss.peek() == EOF}} (everything was consumed) or maybe by looking at {{ss.gcount()}}.
  • Gardener
    Gardener about 5 years
    I like the solution, but using my compiler on OSX, the ss << std::ws sets the fail flag if there is no whitespace to be read. I have to check for eof prior to flushing whitespace.