Error Handling in File I/O in C++

In the file related examples so far we have not concerned ourselves with error situations. In particular, we have assumed that the error handling in file we opened for reading already existed, and that those opened for writing could be created or appended to. We’ve also assumed that there were no failures during reading or writing. In a real program it is important to verify such assumptions and take appropriate action if they turn out to be incorrect.

A file that you think exists may not, or a filename that you assume you can use for a new file may already apply to an existing file. Or there may be no more room on the disk, or no disk in the drive, and so on.

Reacting to Errors

Our next program shows how such error handling in file are most conveniently handled. All disk operations are checked after they are performed. If an error has occurred, a message is printed and the program terminates. We’ve used the technique, discussed earlier, of checking the return value from the object itself to determine its error status.

The program opens an output stream object, writes an entire array of integers to it with a single call to write(), and closes the object. Then it opens an input stream object and reads the array of integers with a call to read().

// rewerr.cpp
// handles errors during input and output
#include <fstream>    //for file streams
#include <iostream>
using namespace std;
using namespace std;
#include <process.h>  //for exit()
const int MAX = 1000;
int buff[MAX];
int main()
{
for(int j=0; j<MAX; j++) //fill buffer with data
buff[j] = j;
ofstream os; //create output stream
//open it
os.open(“a:edata.dat”, ios::trunc | ios::binary);
if(!os)
{ cerr << “Could not open output file\n”; exit(1); }
cout << “Writing...\n”; //write buffer to it
os.write( reinterpret_cast<char*>(buff), MAX*sizeof(int) );
if(!os)
{ cerr << “Could not write to file\n”; exit(1); }
os.close(); //must close it
for(j=0; j<MAX; j++) //clear buffer
buff[j] = 0;
ifstream is; //create input stream
is.open(“a:edata.dat”, ios::binary);
if(!is)
{ cerr << “Could not open input file\n”; exit(1); }
cout << “Reading...\n”; //read file
is.read( reinterpret_cast<char*>(buff), MAX*sizeof(int) );
if(!is)
{ cerr << “Could not read from file\n”; exit(1); }
for(j=0; j<MAX; j++) //check data
if( buff[j] != j )
{ cerr << “\nData is incorrect\n”; exit(1); }
cout << “Data is correct\n”;
return 0;
}

Analyzing Errors

In the REWERR example we determined whether an error occurred in an I/O operation by examining the return value of the entire stream object.

if(!is)
// error occurred

Here is returns a pointer value if everything went well, but 0 if it didn’t. This is the shotgun approach to errors: No matter what the error is, it’s detected in the same way and the same action is taken. However, it’s also possible, using the ios error status flags, to find out more specific information about a file I/O error. We’ve already seen some of these status flags at work in screen and keyboard I/O. Our next example, FERRORS, shows how they can be used in file I/O.

// ferrors.cpp
// checks for errors opening file
#include <fstream>    // for file functions
#include <iostream>
using namespace std;
int main()
{
ifstream file;
file.open(“a:test.dat”);
if( !file )
cout << “\nCan’t open GROUP.DAT”;
else
cout << “\nFile opened successfully.”;
cout << “\nfile = “ << file;
cout << “\nError state = “ << file.rdstate();
cout << “\ngood() = “ << file.good();
cout << “\neof() = “ << file.eof();
cout << “\nfail() = “ << file.fail();
cout << “\nbad() = “ << file.bad() << endl;
file.close();
return 0;
}

This program first checks the value of the object file. If its value is zero, the file probably could not be opened because it didn’t exist. Here’s the output from FERRORS when that’s the case:

Can’t open GROUP.DAT
file = 0x1c730000
Error state = 4
good() = 0
eof() = 0
fail() = 4
bad() = 4

The error state returned by rdstate() is 4. This is the bit that indicates that the file doesn’t exist; it’s set to 1. The other bits are all set to 0. The good() function returns 1 (true) only when no bits are set, so it returns 0 (false). We’re not at EOF, so eof() returns 0. The fail() and bad() functions return nonzero, since an error occurred.

In a serious program, some or all of these functions should be used after every I/O operation to ensure that things went as expected.

Read More Topics
Desk file i/o streams
Static functions in C++
Pointers to arrays
User interface design

About the author

Santhakumar Raja

Hi, This blog is dedicated to students to stay update in the education industry. Motivates students to become better readers and writers.

View all posts

Leave a Reply