Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getline() throws basic_ios::clear exception after reading the last line

I am writing a unit test for file read using qtestlib, C++ (clang LLVM version 8.0). I have the following code for reading a file line by line.

std::ifstream infile;

try {
    infile.open(path.c_str());
    std::ios_base::iostate exceptionMask = infile.exceptions() | std::ios::failbit;
    infile.exceptions(exceptionMask);

} catch (std::ios_base::failure& e) {
    // print the exception
    qDebug() << "Exception caught: " << QString::fromStdString(e.what());
}

try {
    std::string line;
    while (std::getline(infile, line)) {
        // print the line
        qDebug() << QString::fromStdString(line);
    }

} catch (std::ios_base::failure& e) {
    qDebug() << "Exception caught: " << QString::fromStdString(e.what());
}

The issue:

The above code reads all the lines in the file and prints it. But after printing the last line, it throws an exception and prints the following,

Exception caught: "basic_ios::clear"

I followed many threads, but could not find the solution to this. Why am I getting this error?

like image 292
akashrajkn Avatar asked Nov 12 '16 09:11

akashrajkn


2 Answers

After you have read and printed all the lines, the while (std::getline(infile, line)) will still try to read another line. If it fails totally - zero characters read - it sets failbit to signal its failure.

The odd part of the error message is that, despite its name, basic_ios::clear can be used to set the failure bit and will also throw an exception if you have enabled the same bit with exceptions.

like image 69
Bo Persson Avatar answered Oct 05 '22 03:10

Bo Persson


Take a look on documentation of std::getline. Scetion about setting flags:

failbit

The input obtained could not be interpreted as a valid textual representation of an object of this type. In this case, distr preserves the parameters and internal data it had before the call. Notice that some eofbit cases will also set failbit.

The last sentence is a bit fuzzy, but can explain observed behavior.

I did some experiments and found the pattern. First I've corrected your code this way:

try {
    std::string line;
    while (std::getline(infile, line)) {
        // print the line
        qDebug() << QString::fromStdString(line);
        if (infile.eof()) {
            return;
        }
    }

} catch (std::ios_base::failure& e) {
    qDebug() << "Exception caught: " << QString::fromStdString(e.what());
}

Now if input file ends with empty line I get an exception, if last line doesn't end with "\n" return breaks a loop.

So falbit is set if you are trying to read stream which already reached end of stream. Without "if" check you are always doing this reading and always get an exception.

For last line empty I have some clues, but have not idea how to explain it nicely. First have to check behavior of other platforms/compilers.

like image 31
Marek R Avatar answered Oct 05 '22 05:10

Marek R