0

In the following code I do a read from a file containing simply "12345" using std::getline, and immediately after attempt to write "67890" to the file. This write will fail unless I first call myfile.clear(). Why is this? What exactly is breaking? Is there a way to still use std::getline in a loop but prevent an error from occurring when the last line is read? What is correct?

#include <fstream>
#include <iostream>
#include <string>

using std::cout;
using std::fstream;
using std::string;

void printStats(fstream&);

int main(int argc, char* argv[])
{
    // textfile.txt:
    // 1234 
    fstream myfile("textfile.txt", std::fstream::in | std::fstream::out);
    string line;
    
    cout << "\n"
         << "Before reading lines in a loop.\n";
    printStats(myfile);
    
    cout << "\n" 
         << "Reading lines in the loop.\n";
    while (myfile.eof() != true)
    {   
        std::getline(myfile, line);     // Last call here causes EOF and FAIL bit to be set
        cout << "line=" << line << "\n";
    }
    
    cout << "\n" 
         << "After reading lines in a loop.\n";
    printStats(myfile);
    
    myfile.clear(); // If I comment this out the write below fails
    
    myfile << "67890\n";
    
    myfile.close();
    
    return 0;
}

void printStats(fstream& fileStream)
{
    int position = fileStream.tellp();
        cout << "position = " << position << "\n";

    if (fileStream.eof() == true)
        cout << "EOF bit  = 1\n";
    else
        cout << "EOF bit  = 0\n";

    if (fileStream.fail() == true)
        cout << "FAIL bit = 1\n";
    else
        cout << "FAIL bit = 0\n";

    if (fileStream.bad() == true)
        cout << "BAD bit  = 1\n";
    else
        cout << "BAD bit  = 0\n";
}

Here are the results from execution with myfile.clear() commented out:

user@Ubuntu:~/example/test$ cat textfile.txt ; ./test ; cat textfile.txt 
12345

Before reading lines in a loop.
position = 0
EOF bit  = 0
FAIL bit = 0
BAD bit  = 0

Reading lines in the loop.
line=12345
line=

After reading lines in a loop.
position = -1
EOF bit  = 1
FAIL bit = 1
BAD bit  = 0
12345

Here are the results from execution with myfile.clear() included in the code:

user@Ubuntu:~/example/test$ cat textfile.txt ; ./test ; cat textfile.txt 
12345

Before reading lines in a loop.
position = 0
EOF bit  = 0
FAIL bit = 0
BAD bit  = 0

Reading lines in the loop.
line=12345
line=

After reading lines in a loop.
position = -1
EOF bit  = 1
FAIL bit = 1
BAD bit  = 0
12345
67890

This is compiled with g++ on Ubuntu Linux 20.04

user@Ubuntu:~/example/test$ /usr/bin/g++ --version
g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

I did find this similar result below but it does not clearly explain the answers to my questions.

c++ getline() looping when used in file with a read in int array?

Mark L
  • 11
  • 2
  • Good, complete question, but you could have saved yourself some time by checking documentation for `clear` to find out what it does. – user4581301 Nov 18 '20 at 05:17
  • This doesn't address the question, but `while (myfile.eof() != true)` is usually written `while (!myfile.eof())`. If you really, really want to compare to an explicit value, though, it would be clearer to write `while (myfile.eof() == false)`. – Pete Becker Nov 18 '20 at 14:27

1 Answers1

3

Looks like you're running into the issue of why while(!in.eof()) is usually wrong. The eofbit isn't set until after you've attempted to read past the end of the file, which because you're doing so with getline causes the failbit to be set as well. Then the stream refuses to write while myfile.fail() is true, which it is until you clear it.

Nathan Pierson
  • 1,544
  • 1
  • 4
  • 14
  • Yeah but is there any way to use getline where it won't read past the end of the file? – Mark L Nov 18 '20 at 22:13
  • I think you're right, actually. Changing to `while(getline(myfile, line))` would still leave the failbit set and require a call to `myfile.clear()`, it would just have the effect of not `cout`ing a blank line after EOF is reached. But it makes it clearer that you can't expect to never set the failbit by trying to be vigilant about checking for EOF before performing reads. – Nathan Pierson Nov 18 '20 at 22:20
  • Yeah it just seemed strange that I need to clear an error setting when I'm doing what seems like a perfectly normal operation. I have always found C++ streams to be cumbersome and unintuitive compared to working with files in C. Anyway I guess that answers my question. Any time you read past the end of the file you have to clear the fail bit. – Mark L Nov 20 '20 at 22:28