|
 |
 |
[an error occurred while processing this directive]
Steps
The steps presented are generic rules for ensuring that your code will behave as expected:
- 1. When reading from a stream with a loop, use code such as
while ( infile >> variable )
Because the input stream is checked for validity each time the variable is extracted, the condition for the loop will be false as soon as it attempts to read past the end of the stream.
- 2. When reading data from a stream whose format youre not familiar with, use a character array. Until the behavior differences are sorted out between the STL implementations (which should be soon), it is better to read an array of characters from a stream, and use it to initialize a string object to work with.
- 3. Be aware that one stream error condition is not getting data of the type expected from the stream. If you try to extract an int from a stream when the next item in a stream is a set of non-numerical characters, the stream will stop reading there. If you want to handle this in your program instead, read to a temporary character array, and work with that (strstreams and STL strings have internal conversion functions, in addition to the runtime library functions).
How It Works
Streams typically only set error conditions if the stream cannot convert the next item in the stream to the type requested, or if an attempt is made to read past the end of the stream. Reading up to the end of a stream will not set the error condition.
15.3 Handle stream errors?
Problem
A stream processing loop stops reading from a file when it encounters an error. This How-To details a way to handle stream errors gracefully.
Technique
In the previous How-To, it was explained that streams only set error conditions when reading unexpected input, or when trying to read past the end of a stream.
The goal now is to deal with the error if it is expected, handle it, and then continue processing.
Stream Error Conditions
IOStreams classes define only three error conditions:
eof: The stream has arrived at the end of the file. This is only set for streams that read.
fail: This is the catch-all error. This can be caused by something as simple as bad input to a hard failure.
bad: The stream had a hard failure. This is usually something beyond the programs control, such as the operating system returning an error, or the file no longer being available (for example, a network connection through which the file was accessed was lost). When writing to a file, this error could mean that the drive the file is on ran out of disk space.
How these errors are handled varies from program to program. Usually handling the bad error state is pretty involved, and varies from operating system to operating system. When dealing with the operating system directly, getting the file descriptor might be useful. This can be done (for file streams) by calling the <filestream name>.rdbuf()->fd() function to get the file descriptor held by the underlying buffer object.
Steps
An effective method for handling streams errors is one that does not affect the readability of the code. The idea is to keep the stream processing as clear as possible to anyone else reading the code and handle the error elsewhere. I tend to put this into a function to keep the potentially ugly code from polluting an otherwise transparent processing loop.
These steps assume that an error condition is handled within a reading loop, but the technique is applicable everywhere.
- 1. Create a function definition at the beginning of your C++ program file, or in another module, if that is what you prefer.
The prototype should look like this:
bool handle_stream_error( istream& );
If your code deals with an output stream, use an ostream reference instead.
- 2. In the file, find the area where the stream is being read from.
Where the code is
while( infile >> indata)
change it to
while(handle_stream_error( infile >> indata ) )
The effect of adding a function here minimizes the impact of the error handling code on overall readability.
- 3. Implement the handle_stream_error function as you see fit. For this example, the class handles the three error types, and issues messages on the ones it either cant or should not recover from. The following code shows the implemented handle_stream_error() function from qread2.cpp:
bool handle_stream_error( istream& strm )
{
// handle our favorite error condition, which is
// No error :)
if ( strm.good() ) return true ;
else if ( strm.eof() )
{
// handle end of file condition
cerr << \nEnd of file reached\n ;
// note that endl is not used, as potentially the
// calling code could want to output something nicely
// formatted before this message appears on the screen
return false ;
}
else if ( strm.fail() )
{
// encompasses the bad condition, so we need to distinguish
if ( strm.bad() )
{
cerr << \nUnrecoverable Failure.\n ;
return false ;
}
// we can assume that the error state is invalid input, so
// well handle this by resetting the stream, and pulling
// one character out and returning ok.
strm.clear() ;
while( !isdigit( strm.peek()) && strm )
int useless = strm.get() ;
return true ;
}
// we fell though the loop for some reason, and some compilers
// spit out a warning if the following is not there:
return false ;
}
- 4. Change the processing loop slightly. The preceding error handling function is designed to ignore all characters from the stream. However, when the stream gets an invalid character, it fails first, causing nothing to be read, and leaving the numeric value untouched. This means the processing loop might need to deal with repetitive input. The function could be made to deal with that, but it would detract from the functions inherent reusability (reusable except for one line). The following code shows the reworked processing loop from qread2.cpp:
if( indata != -1 )
{
cout << indata << ;
indata = -1 ;
}
In this case, it was easiest to invalidate the data read from the file and check to see whether the value was changed. Depending on the data, it might be easier to keep the previous value and compare the two. Note that this change does introduce one possible problem: Should a 1 value be read from the file, it will not be processed.
|