Streams and Files - II


The following topics are covered in this section:


Stream Status

To determine the present state of a stream, there are four bits (or flags) associated with every stream. They are:

Stream Status Flag

Purpose

badbit

Set when a fatal error occurs.

eofbit

Set when the end of the stream is encountered (if stream relates to a file then it is set when end of file encountered)

failbit

Set when non-fatal error occurs (for example when invalid data is stored)

goodbit

It is set if there are no errors.

Let’s see a very simple illustration of how you can make use of these bits. Let us create a class called ‘date’ which will be used to obtain the date from the user in the following pattern: DD-MM-YEAR

To achieve this we shall overload the >> operator.

#include <iostream.h>
class date
{private:
    int day,month,year;
public:
date( ) //constructor
{
day=month=year=0;
}
void disp( )
{
cout<<endl<<"The date is : ";
cout<<day<<"-"<<month<<"-"<<year;
}
friend istream& operator>>(istream&, date&);
};
istream& operator>>(istream &istr, date &dt)
{char dash;
cout<<endl<<"Enter the date (separated by - ): ";
istr>>dt.day;
istr>>dash;
if (dash != '-')
{
istr.setstate(ios::failbit); //setting stream status
}
istr>>dt.month;
istr>>dash;
if (dash != '-')
{
istr.setstate(ios::failbit); //setting stream status
}
istr>>dt.year;
if (istr.fail( )) //checking stream status
{
cout<<endl<<"Error in the format of the date entered!";
}
return istr;
}
int main( )
{date d;
cin>>d;
d.disp( );
return 0;
}

The output if the input is typed correctly is:

Enter the date (separated by - ): 11-10-1981
The date is : 11-10-1981

The output if the input is typed incorrectly is as follows:

Enter the date (separated by - ): 11/10-1980
Error in the format of the date entered!
The date is : 11-0-0

The function used to set the ‘failbit’ is:

    setstate( )

(Some compilers might not support this function). We have set the ‘failbit’ when the separator used by the user is something other than the ‘-’ character. As soon as the ‘failbit’ is set all further stream operations are ignored till the error is corrected (which is the reason why even ‘1980’ is not accepted by the program). The function:

istr.fail( )

is used to read the status of a stream. It will be discussed in the next section.

Reading the status of a stream:

We have learnt about the stream status flags and we have even discussed as to how the flags could be set. To identify which status bit has been set we can make use of corresponding member functions.

Function

Stream Status Flag checked

int bad ( )

badbit

int eof ( )

eofbit

int fail ( )

failbit

int good ( )

goodbit

‘cin’ is a stream and even its ‘failbit’ can get set as shown in the program below:

#include <iostream.h>
int main( )
{ short int num;
cout<<"Enter a short integer : ";
cin>>num;
if (cin.fail( ))
{
cout<<endl<<"Error in input";
cout<<endl<<"The number stored is :"<<num;
}
else
{
cout<<endl<< "Valid value entered.";
}
return 0;
}

The output for a value that exceeds the short integer range will be:

Enter a short integer : 56432
Error in input
The number stored is :32767

The fail ( ) function will return a non-zero value if the number entered by the user exceeds the limit of a short integer. If this is the case then the ‘if’ condition is satisfied and the loop is executed.

Usually we will not be using the stream status flags as shown in the above case. The same concept will be applied for files (because files are also accessed via streams and all the points discussed here are relevant for files).

Remember: Once any of the error bits are set, they will continue to remain set till they are cleared.

For example:

#include <iostream.h>
int main( )
{ short int num;
int num2;
cout<<"Enter a short integer : ";
cin>>num;
if ( cin.fail( ) )
{
cout<<endl<<"Error in input";
cout<<endl<<"The number stored is :"<<num;
}
cout<<endl<<endl<<"Enter an integer:";
cin>>num2;
cout<<endl<<"The failbit value is : "<<cin.fail( );
return 0;
}

The output if a proper value is entered is:

Enter a short integer : 20
Enter an integer:24
The failbit value is : 0

The output for a higher value is:

Enter a short integer : 45345
Error in input
The number stored is :32767
Enter an integer:
The failbit value is : 2

In the second case, the ‘failbit’ for the stream ‘cin’ is set to 2 (because a very high number was entered initially). This bit hasn’t been reset and thus ‘cin’ cannot be used to obtain the value for the second integer (the program will not allow the user to enter a value because the ‘cin’ stream has an error).

The modified program will be:

#include <iostream.h>
int main( )
{ short int num;
int num2;
cout<<"Enter a short integer : ";
cin>>num;
if ( cin.fail( ) )
{
cout<<endl<<"Error in input";
cout<<endl<<"The number stored is :"<<num;
}
cin.clear( );
cout<<endl<<"All error flags cleared.";
cout<<endl<<endl<<"Enter an integer:";
cin>>num2;
cout<<endl<<"The failbit value is : "<<cin.fail( );
return 0;
}

The output is:

Enter a short integer : 1232345
Error in input
The number stored is :32767
All error flags cleared.
Enter an integer:2
The failbit value is : 0

If no argument is specified for clear ( ) function then all the error bits are cleared. After the clear ( ) fucntion is executed, good ( ) function will return a TRUE value (because there are no errors now). Suppose you want to set the ‘failbit’ and reset the other bits, you can type:

stream-name.clear(ios::failbit);

This will reset all the error bits and will set the ‘failbit’. Similarly you can set other bits also.


Opening and Closing a ‘File’

So far we have dealt with standard I/O. Next we shall deal with file I/O and to access any device you have to make use of another header file: ‘fstream.h’. This header file has a number of classes already defined. To access a file you have to have a stream. We have already come across the classes istream, ostream and iostream. From these classes, 3 more classes are derived (they are ofstream, ifstream, and fstream) and these classes are specifically useful for streams used in ‘file’ operations. The hierarchy of classes will be as shown in the figure.

As can be seen, the ‘ifstream’, ‘ofstream’ and ‘fstream’ classes are derived from the ‘istream’, ‘ostream’ and ‘iostream’ classes. These are file streams that are derived from the general I/O streams that we have seen earlier.

ifstream in;             // file stream named ‘in’ created for handling input
ofstream out;         // file stream named ‘out’ created for handling output
fstream both;         // file stream named ‘both’- can handle both input and output

Once you've created a stream, you can use the open ( ) function to associate it to a ‘file’. To associate the stream to a disk file, we should specify the name of the disk file. The open ( ) function is a member available in all the three classes. It can be used as follows:

out.open("text.txt") ;         // Opens a file called text.txt for output.

Remember: When you say that a file is opened for output, it actually means that now you can write data to the file. When a file is opened for input (i.e. using ifstream), the data in the file can be displayed on the screen.

What if there already is a file called text.txt. When using the output stream (ofstream), the stream will create a new file text.txt. Whatever content was there in the original text.txt gets deleted and you can write new data to text.txt.

The 3 classes (ofstream, ifstream, fstream) have a constructor function that makes it easier to open a file. Example:

ofstream out("text.txt");

This creates an object out for output and opens the file text.txt in one single statement. Here we don’t make use of the open ( ) function.


Closing a File

Anything that you open has to be closed. The member function for closing is close ( ). Since you do all I/O through the stream, you have to close the stream as follows:

stream-name.close( );

Actually you can link this back to the object and classes concept. ‘stream-name’ is an object and close is a member function of the ofstream class. Hence by saying

stream-name.close( );

you're actually invoking the member function close( ). A stream is associated to a device when using the open function. When the close ( ) function is used, the stream is disassociated from the device.


Can Errors occur while opening a file?

When you open a file using ofstream (which means for output), you can write data to the file. You could say it's an input to the file. Hence open for output means actually for input.

When a file is opened for reading, you will make use of the ifstream as follows:

ifstream instr("test.txt");         // The file test.txt is opened for reading

Beware: Many beginners confuse between the use of ‘ifstream’ and ‘ofstream’ objects. Be clear as to what you want to use.

When you want to read a file, it implies that the file is already present in your directory. What will happen if we attempt to open a file that is not present?

This will cause an error and you should provide the necessary coding to deal with this situation. When you open a file for writing data, the stream will create the file even if it doesn't exist. But if you attempt to open a file for reading data and if it isn't present in the computer, this will cause an error. You should always check whether an error has resulted while using the open operation as follows (‘instr’ is an object of type ifstream):

if ( ! instr )
{
cout<< "The file cannot be opened";
}

if ( ! instr ) stands for : ‘if not instr’ (that means ‘if instr not open’) then do what is said in the body of the if statement.


Go back to the Contents Page 2


Copyright © 2004 Sethu Subramanian All rights reserved.