Use getline() without setting failbit
Asked Answered
H

1

9

Is it possible use getline() to read a valid file without setting failbit? I would like to use failbit so that an exception is generated if the input file is not readable.

The following code always outputs basic_ios::clear as the last line - even if a valid input is specified.

test.cc:

#include <iostream>
#include <string>
#include <fstream>
using namespace std;

int main(int argc, char* argv[])
{
    ifstream inf;
    string line;

    inf.exceptions(ifstream::failbit);
    try {
        inf.open(argv[1]);
        while(getline(inf,line))
            cout << line << endl;
        inf.close();
    } catch(ifstream::failure e) {
        cout << e.what() << endl;
    }
}

input.txt:

the first line
the second line
the last line

results:

$ ./a.out input.txt 
the first line
the second line
the last line
basic_ios::clear
Habakkuk answered 21/10, 2011 at 20:59 Comment(4)
When do you expect the failbit to be raised when using getline()?Honewort
If the file does not exist. eg ./a.out nosuchfile.txtHabakkuk
For that you can check is_open() after you open the file.Honewort
I just ran into this. Seems to me it should set eofbit, not failbit. Annoying.Bifacial
N
10

You can't. The standard says about getline:

If the function extracts no characters, it calls is.setstate(ios_base::failbit) which may throw ios_base::failure (27.5.5.4).

If your file ends with an empty line, i.e. last character is '\n', then the last call to getline reads no characters and fails. Indeed, how did you want the loop to terminate if it would not set failbit? The condition of the while would always be true and it would run forever.

I think that you misunderstand what failbit means. It does not mean that the file cannot be read. It is rather used as a flag that the last operation succeeded. To indicate a low-level failure the badbit is used, but it has little use for standard file streams. failbit and eofbit usually should not be interpreted as exceptional situations. badbit on the other hand should, and I would argue that fstream::open should have set badbit instead of failbit.

Anyway, the above code should be written as:

try {
    ifstream inf(argv[1]);
    if(!inf) throw SomeError("Cannot open file", argv[1]);
    string line;
    while(getline(inf,line))
        cout << line << endl;
    inf.close();
} catch(const std::exception& e) {
    cout << e.what() << endl;
}
Naturism answered 21/10, 2011 at 22:1 Comment(1)
Thanks for explaining failbit limitations. Your implementation works.Habakkuk

© 2022 - 2024 — McMap. All rights reserved.