Kafeaulait
Kafeaulait

Reputation: 684

C++ istream operator>> bad-data handling

Every time I ask a question here on SO, it turns out to be some very dumb mistake (check my history if you don't believe me), so bear with me if you can here.

It feels like my question should be very popular, but I couldn't find anything about it and I've run out of ideas to try.

Anyway, without further ado:


I'm trying to overload the input operator>>. It's supposed to read one integer at a time from a file, skipping invalid data such as chars, floats, etc.

Naturally, I'm checking if(in >> inNum) to both get() the next token and check for successful get().
If successful, not much to say there.
If it fails, however, I assume that one of two things happened:

  1. It stumbled upon a non-integer
  2. It reached the eof

Here's how I tried to deal with it:

istream& operator>> (istream& in, SortSetArray& setB) {
    bool eof = false;
    int inNum = -1;
    while(!eof) {
        if(in >> inNum) {
            cout << "DEBUG SUCCESS: inNum = " << inNum << endl;
            setB.insert(inNum);
        }
        else {
            // check eof, using peek()
            // 1. clear all flags since peek() returns eof regardless of what
            //    flag is raised, even if it's not `eof`
            in.clear();
            cout << "DEBUG FAIL: inNum = " << inNum << endl;
            // 2. then check eof with peek()
            eof = (in.peek() == std::char_traits<char>::eof());
        }
    }
    return in;
}

The file contains [1 2 3 4 a 5 6 7], and the program naturally goes into infinite loop. Okay, easy guess, peek() doesn't consume the char 'a', and maybe in >> inNum also failed to consume it somehow. No biggie, I'll just try something that does.

And that's pretty much where I've been for the last 2 hours. I tried istream::ignore(), istream::get(), ios::rdstate to check eof, double and string instead of char in the file, just in case char is read numerically.

Nothing works and I'm desperate.

Weirdly enough, the approach above worked for a previous program where I had to read a triplet of data entries on a line of the format: string int int
The only difference is I used an ifstream object for that one, and an istream object for this one.

Bonus Question: inNum has the value of 0 when the hiccup occurs. I'm guessing it's something that istream::operator>> does?

Upvotes: 0

Views: 838

Answers (1)

Filip Ros&#233;en
Filip Ros&#233;en

Reputation: 63832

Implementation description

  1. try to read an int
  2. if successful;
    1. insert the read value to setB
    2. next iteration
  3. else;
    1. clear error flags
    2. check so that we haven't reached the end of the file
    3. still more data? next iteration.

The above is the logic description of your function, but there's something missing...

In case we try to read a value, but fail, std::istream's handle these cases by setting the approriate error flags, but it will not discard any data.

The problem with your implementation is that upon trying to read invalid data, you will just try to read the same invalid data again.. over, and over, and over, inf.


Solution

After clearing the error flags you can use std::istream::ignore to discard any data from the stream.

The function's 1st argument is the max number of potential chars to ignore, and the 2nd is the "if you hit this char, don't ignore any more*.

Let's ignore the maximum amount of characters, or until we hit ' ' (space):

#include <limits> // std::numeric_limits

in.ignore (std::numeric_limits<std::streamsize>::max(), ' ');

Upvotes: 1

Related Questions