Why getline() throws 'std::ios_base::failure' when exception mask is not set to eofbit?
Asked Answered
N

1

9

Consider the following code:

    ifstream in;
    try {
        in.exceptions ( ifstream::failbit | ifstream::badbit );
        in.open(pConfLocation);
    } catch ( ifstream::failure e ) {
        throw std::runtime_error("Can't open configuration file\n");
    }

    vector<string> lns;
    string s;

    in.clear();
    while ( !in.eof() ){
        getline( in, s );
        boost::algorithm::trim(s);
        lns.push_back( s+='\n');
    }

So:

  1. I set the following "exception mask" ( ifstream::failbit | ifstream::badbit ) for the needs of the try-catch block. The file opens without problems.
  2. In while{} block, I know that at the end of the file eofbit will be set. But

The exception mask is an internal value of all stream objects specifying which state flags have to throw an exception when they are set.

I did NOT set ifstream::eofbit, but anyway the following error appears at runtime:

terminate called after throwing an instance of 'std::ios_base::failure'
  what():  basic_ios::clear
The program has unexpectedly finished.

I cannot understand this behavior. I tried to use in.clear() right before while{} but with no effect. clear() itself sets goodbit, and as far as I understand, "flags have to throw an exception" (see the quote above), but when googbit set it's not causing to throw any exceptions…

If to delete

        in.exceptions ( ifstream::failbit | ifstream::badbit );

it works.


How to make getline() work in this case?

Noticeable answered 30/11, 2012 at 18:34 Comment(0)
G
8

The problem is in your input iteration. eofbit is only set when the last read reached EOF, not if the next read will read only EOF. When the latter happens, failbit is set at the same time. See the discussion here.

In your particular case, if the file ends with a newline (as it probably does), the getline() reads up to and including that newline and returns. eofbit is still not set. The next getline() then encounters EOF directly, and as per its documentation, "If the function extracts no elements, it calls setstate(failbit)."

Guyton answered 30/11, 2012 at 18:38 Comment(9)
Ok, but I clear()-ed exception mask, why is it still seems to be active?Noticeable
@Noticeable There is no clean() member, I assume you meant clear(). This clears the state bits (eofbit, failbit and badbit), but does not affect the exception mask.Guyton
Yes, I used clear() sorry [fixed in Q]. Probably the last one question related to the problem: is there any way to return to state preceding setting of the exception mask?Noticeable
@Noticeable I don't understand what you mean now. Do you want to clear the exception mask? If so, you can do in.exceptions(ifstream::goodbit);Guyton
I mean I dont understand why if to remove "in.exceptions ( ifstream::failbit | ifstream::badbit )" there will be no errors visible errors in this code.Noticeable
If the exception is not thrown, the last getline() (the one which sets failbit) will clear s and you'll process an empty string (push "\n" into lns) which was never part of the input. In other words, you're getting bogus data.Guyton
It is worth mentioning the correct loop form to use: while (getline( in, s )) { boost::algorithm::trim(s); lns.push_back( s+='\n'); }Onassis
I met the same problem. I don't quite get how you guys solve the problem while keeping exceptions flags set for try/catch block.Antimonic
@LingboTang You have to have a clear idea of what is "reaching EOF" and what is "failure" in stream terminology (I suggest reading some iostate reference). If you really want exceptions for failure but not for reading past the last line in a file, you have to use peek before reading.Guyton

© 2022 - 2024 — McMap. All rights reserved.