2

I am using the standard C++ fstreams library and I am wondering what is the right way to use it. By experience I sort of figured out a small usage protocol, but I am not really sure about it. For the sake of simplicity let's assume that I just want to read a file, e.g., to filter its content and put it on another file. My routine is roughly as follows:

  • I declare a local istream i("filename") variable to open the file;
  • I check either i.good() or i.is_open() to handle the case where something went bad when opening, e.g., because the file does not exist; after, I assume that the file exists and that i is ok;
  • I call i.peek() and then again i.good() or i.eof() to rule out the case where the file is empty; after, I assume that I have actually something to read;
  • I use >> or whatever to read the file's content, and eof() to check that I am over;
  • I do not explicitly close the file - I rely on RAII and keep my methods as short and coherent as I can.

Is it a sane (correct, minimal) routine? In the negative case, how would you fix it? Please note that I am not considering races - synchronization is a different affair.

Pietro Braione
  • 1,123
  • 8
  • 22

2 Answers2

4

I would eliminate the peek/good/eof (your third step). Simply attempt to read your data, and check whether the attempted read succeeded or failed. Likewise, in the fourth step, just check whether your attempted read succeeded or not.

Typical code would be something like:

std::ifstream i("whatever");

if (!i)
    error("opening file");

while (i >> your_data)
    process(your_data);

if (!i.eof())
   // reading failed before end of file
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • I would not. One of the thing sometimes do is something like i >> another_stream. If I do not check for an empty file, then both i and another_stream have the failbit set and I lose the ability to write on another_stream. – Pietro Braione Mar 08 '13 at 15:58
  • @Pietro: In that *very* specific circumstance, checking for an empty file *might* make sense. Alternatively, you can just reset `another_stream` to regain the ability to write to it. – Jerry Coffin Mar 08 '13 at 16:00
3

It's simpler than you have described. The first two steps are fine (but the second is not necessary if you follow the rest of my advice). Then you should attempt extraction, but use the extraction as the condition of a loop or if statement. If, for example, the file is formatted as a series of lines (or other delimited sequences) all of the same format, you could do:

std::string line;
while (std::getline(i, line)) {
  // Parse line
}

The body of the loop will only execute if the line extraction works. Of course, you will need to check the validity of the line inside the loop.

If you have a certain series of extractions or other operations to do on the stream, you can place them in an if condition like so:

if (i >> some_string &&
    i.get() == '-' &&
    i >> some_int) {
  // Use some_string and some_int
}

If this first extraction fails, the i.ignore() not execute due to short-circuit evaluation of &&. The body of the if statement will only execute if both extractions succeed. If you have two extractions together, you can of course chain them:

if (i >> some_string >> some_int) {
  // Use some_string and some_int
}

The second extraction in the chain will not occur if the first one fails. A failed extraction puts the stream in a state in which all following extractions also fail automatically.

For this reason, it's also fine to place the stream operations outside of the if condition and then check the state of the stream:

i >> some_string >> some_int;
if (i) {
  // Use some_string and some_int
}

With both of these methods, you don't have to check for certain problems with the stream. Checking the stream for eof() doesn't necessarily mean that the next read will fail. A common case is when people use the following incorrect extraction loop:

// DO NOT DO THIS
while (!i.eof()) {
  std::getline(i, line)
  // Do something with line
}

Most text files end with an extra new line at the end that text editors hide from you. When you're reading lines from the text file, for the last iteration you haven't yet hit the end of file because there's still a \n to read. So the loop continues, attempts to extract the next line which doesn't exist and screws up. People often observe this as "reading the last line of the file twice".

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324