1

When I try to extract valid numbers from an input using istringstream I get the follwoing misbehavior from istringstream:
For Example:

void extract(void)
{
double x;
string line, temp;

getline(cin, line);
istringstream is(line);
while(is >>temp)
{
    if(istringstream(temp) >>x)
        {std::cout<<"number read: "<<x<<endl;}
}

}

Input: 1 2 3rd 4th

Output:

number read: 1
number read: 2
number read: 3
number read: 4

The misbehavior is istringstream converting the string 3rd to the number 3.
Why does istringstream do this and how can one avoid this?

Andy_A̷n̷d̷y̷
  • 795
  • 2
  • 11
  • 25
  • 1
    You're reading a `double` from the stream, not strings. – Lord Zsolt Dec 11 '14 at 11:20
  • @LordZsolt can you please explain more, I'm still confused. Also the same thing happens when I use `atof()` – Andy_A̷n̷d̷y̷ Dec 11 '14 at 11:22
  • @Andy `atof` is broken; it provides no means of error checking. If you use `strtod`, you can recover the address of the first character not processed; if that isn't `'\0'`, you haven't processed all of the text. – James Kanze Dec 11 '14 at 12:12
  • If you want the input as a string, then the inner `if` statement is not needed. In fact the expression used as the condition in the `if` statement will not work as you expect, because it will extract only the number-part of a string that begins with a number bot doesn't end with a string. If you need to distinguish between pure numbers or strings like `"3rd"` then you need to use e.g. [`std::stod`](http://en.cppreference.com/w/cpp/string/basic_string/stof) which can check if the whole string was converted to a number or not. – Some programmer dude Dec 11 '14 at 12:25

2 Answers2

3

It's because you read numbers from the stream.

The >> operator extracts "3rd" from the stream, and tries to convert it to a double, but since only the first character of the string is a number, it can only parse the "3" and simply discard the non-digit characters.

If you want "3rd" then you need to read it as a string.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • What if I want to throw an `invalid_argument` error if the input has something like `3rd`, how would I preceded on doing that knowing the fact you stated above? @JoachimPileborg – Andy_A̷n̷d̷y̷ Dec 11 '14 at 11:25
  • @Andy You might want to read e.g. [this reference](http://en.cppreference.com/w/cpp/locale/num_get/get) on how the extraction actually happens. The problem is that the extracted string *begins* with a digit, and since that can be parsed as a number the stream will not have its `failbit` set. – Some programmer dude Dec 11 '14 at 11:31
  • This is wrong (or I've misunderstood it). When inputting to a `doubled`, the `>>` does _not_ extract `"3rd"`, it will only extract the `3`. The `"rd"` will remain in the stream, available for further reads. – James Kanze Dec 11 '14 at 12:11
0

The >> operators only read as much as they can in the stream, leaving any remaining characters. Thus:

std::istringstream( temp ) >> x

will only extract the 3 if temp contains 3rd, leaving the rd. In order to ensure that the entire field has been read, you need to ensure that you are at the end:

std::istringstream fieldParser( temp );
if ( fieldParser >> x && fieldParser.peek() == EOF ) {
    // ...
}

for example. (More generally, you'll want to append a >> std::ws to the end of the input chain, to skip any trailing white space. In your case, however, since you get temp using the >> operator, you're guaranteed that it contains no white space.)

FWIW: in this particualar case, I'd use strtod:

errno = 0;
const char* end;
x = strtod( temp.c_str(), &end );
if ( errno != 0 || *end != '\0' ) {
    //  Error occured...
}

Either way, however, you can't do it in a single if; you'll need extra code before.

James Kanze
  • 150,581
  • 18
  • 184
  • 329