What State Is a C++ File In?

By Stephen R. Davis

A constructed C++ fstream object (including ifstream and ofstream) becomes a proxy for the file that it is associated with. For example, the stream object maintains state information about the I/O process. The member function bad() returns true if something “bad” happens.

That nebulous term means that the file couldn’t be opened, some internal object was messed up, or things are just generally hosed. A lesser error fail() indicates that either something bad() happened or the last read failed — for example, if you try to read an int and all the program can find is a character that rates a fail() but not a bad(). The member function good() returns true if both bad() and fail() are false.

Attempts to input from or output to a stream object that has an error set are ignored. The member function clear() zeros out the fail flag to give you another chance if the error is temporary — in general, clear() clears “failures” but not “bad” things. All attempts to output to an ofstream object that has an error have no effect.

This last paragraph is meant quite literally — no input or output is possible as long as the internal error state of the stream object you’re using is non-zero. The program won’t even try until you call clear() to clear the error flags if the error is temporary and you can clear it.

The following example program demonstrates how to go about using the ifstream class to extract a series of integers:

// StreamInput - simple input from a file using fstream
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
using namespace std;
ifstream& openFile()
{
    ifstream* pFileStream = 0;
    for(;;)
    {
        // open the file specified by the user
        string sFileName;
        cout << "Enter the name of a file with integers:";
        cin >> sFileName;
        //open file for reading
        pFileStream = new ifstream(sFileName.c_str());
        if (pFileStream->good())
        {
            pFileStream->seekg(0);
            cerr << "Successfully opened "
                 << sFileName << endl;
            break;
        }
        cerr << "Couldn't open " << sFileName << endl;
        delete pFileStream;
    }
    return *pFileStream;
}
int main(int nNumberofArgs, char* pszArgs[])
{
    // get a file stream
    ifstream& fileStream = openFile();
    // stop when no more data in file
    while (!fileStream.eof())
    {
        // read a value
        int nValue = 0;
        fileStream >> nValue;
        // stop if the file read failed (probably because
        // we ran upon something that's not an int or
        // because we found a newline with nothing after
        // it)
        if (fileStream.fail())
        {
            break;
        }
        // output the value just read
        cout << nValue << endl;
    }
    cout << "Press Enter to continue..." << endl;
    cin.ignore(10, 'n');
    cin.get();
    return 0;
}

The function openFile() prompts the user for the name of a file to open. The function creates an ifstream() object with the specified name. Creating an ifstream object automatically opens the file for input. If the file is opened properly, the function returns a reference to the ifstream object to use for reading.

Otherwise, the program deletes the object and tries again. The only way to get out of the loop is to enter a valid filename or abort the program.

Don’t forget to delete the pFileStream object if the open fails. These are the sneaky ways that memory leaks creep in.

The program reads integer values from the object referenced by fileStream until either fail() or the program reaches the end-of-file as indicated by the member function eof().

One last warning: Not only is nothing returned from reading an input stream that has an error, but also the buffer comes back unchanged. This program can easily come to the false conclusion that it has just read the same value it previously read. Furthermore, eof() will never return a true on an input stream that has an error.