Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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;}
like image 773
alan2here Avatar asked Feb 07 '11 00:02

alan2here


3 Answers

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.

like image 82
Fred Nurk Avatar answered Oct 23 '22 17:10

Fred Nurk


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
like image 33
Daniel Hanrahan Avatar answered Oct 23 '22 18:10

Daniel Hanrahan


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;}
}
like image 20
Mark B Avatar answered Oct 23 '22 18:10

Mark B