10

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;}
alan2here
  • 3,223
  • 6
  • 37
  • 62
  • 2
    Does "12.5 abc" qualify as "a string is numerical"? If "no", none of the answers so far apply. – etarion Feb 07 '11 at 01:00

4 Answers4

8

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.

Fred Nurk
  • 13,952
  • 4
  • 37
  • 63
7

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
Daniel Hanrahan
  • 4,801
  • 7
  • 31
  • 44
  • 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. – Gardener Mar 21 '19 at 10:27
5

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;}
}
Mark B
  • 95,107
  • 10
  • 109
  • 188
  • How does "it knows it's trying to parse input" matter at all? Perhaps good for code clarity, but I see no technical reason. – Fred Nurk Feb 07 '11 at 00:52
  • 1
    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()}}. – Tom Feb 07 '11 at 03:53
-1
int str2int (const string &str) {
  stringstream ss(str);
  int num;
  if((ss >> num).fail())
  { 
      //ERROR: not a number
  }
  return num;
}
Julio Gorgé
  • 10,056
  • 2
  • 45
  • 60