Section 6.1
Input/Output with files
|
 |
 |
The fstream library discussed in this section is included in compilers
previous to the ANSI-C++ standard. The specifications of this standard have introduced
some changes on the traditional implementation of this library that have been followed
(although simplified) in the processing of this document.
|
C++ incorporates support for input and output with files through the following classes:
- ofstream: File class for writing operations (derived from ostream)
- ifstream: File class for reading operations (derived from istream)
- fstream: File class for both reading and writing operations (derived from iostream)
Open a file
The first operation generally done on an object of one of these classes is to associate it
to a real file, that is to say, to open a file with him. The open file is represented
within the program by the stream (the object of one of these classes) and any
input or output performed on it will apply to the physical file.
In order to open a file it is used the member function open():
void open (const char * filename, openmode mode);
where filename is a string of characters representing the name of the
file to be opened and mode is a combination of the following flags:
| ios::in | Open file for reading |
| ios::out | Open file for writing |
| ios::ate | Incial position: end of file |
| ios::app | Every output is appended at the end of file |
| ios::trunc | If the file already existed it is erased |
| ios::binary | Binary mode |
These flags can be combined using bitwise operator OR: |.
For example, if we want to open the file "example.bin" in binary mode to add data
we could do it by the following call to function-member open:
ofstream file;
file.open ("example.bin", ios::out | ios::app | ios::binary);
The member functions open of classes
ofstream, ifstream and fstream
include all of them a default mode that varies from one to the other:
| class | default mode to parameter |
| ofstream | ios::out | ios::trunc |
| ifstream | ios::in |
| fstream | ios::in | ios::out |
The default value is only applied if the function is called without the
mode parameter. If the function is called with any value in that parameter
the default mode is stepped on, never combined.
Since frequently the first task that is performed on an object of classes
ofstream, ifstream and fstream
is to open a file, the three include a constructor that calls directly to this
member function and that has the same parameters as this one. This way, we could also
have declared the previous object and conducted the same opening operation just writing:
ofstream file ("example.bin", ios::out | ios::app | ios::binary);
Both forms to open a file are valid.
You can check if a file has been correctly opened by calling to the member function
is_open():
bool is_open();
that returns a bool type value indicating true in case that
indeed the object is correctly associate with an open file or false if
on the opposite it is not.
Close a file
When reading, writing or consult operation on a file have finalized we must close it
so that it become again available. For it, it will be enough to call the member function
close(), that will be in charge to flush the buffers and to close the file.
Its form is quite simple:
void close ();
Once called this member function, the object can be used to open another file.
In case that an object is destructed and is still associated to an open file,
the destructor will automatically call to member function close.
Text mode files
Classes
ofstream, ifstream and fstream
are derived (indirectly) from
ostream, istream and iostream
respectively. Reason why fstream objects can use the members of these last classes
to access to data.
Generally, with text files they are used the same members of these classes that
for the communication with console. Like in the following example, where we use
the overloaded insertion operator <<:
// writing on a text file
#include <fstream.h>
int main () {
ofstream examplefile ("example.txt");
if (examplefile.is_open())
{
examplefile << "This is a line.\n";
examplefile << "This is another line.\n";
examplefile.close();
}
return 0;
}
|
This is a line.
This is another line.
|
Data input from file can also be performed in the same way that with
cin:
// reading a text file
#include <iostream.h>
#include <fstream.h>
int main () {
char buffer[256];
ifstream examplefile ("example.txt");
if (! examplefile.is_open())
{ cout << "Error opening file"; exit (1); }
while (! examplefile.eof() )
{
examplefile.getline (buffer,100);
cout << buffer << endl;
}
return 0;
}
|
This is a line.
This is another line.
|
This last example reads a text file and prints out its content on the screen.
Notice how we have used a new member function, called eof
that ifstream inherits from class ios and that
returns true in case that the end of the file has been reached.
Verification of state flags
Moreover than eof() they exist other member functions
to verify the state of the stream (all of them return a bool value):
- bad()
- Returns true if any failure occurs in a reading or writing operation.
For example in case that it is tried to write to a file that is not open for writing
(the same with reading) or if the device where it is tried to write has
no space left.
- fail()
- Returns true in the same cases than bad() plus
in case that happens a format error, like if it is tried to read an integer number and
an alphabetical character is received.
- eof()
- Returns true if a file opened for reading has arrived to the end.
- good()
- It is the most generic: returns false if when calling to anyone
of the previous ones it would be received true in anyone.
In order to reset the state of the flags checked by the previous member functions
you can use the member function clear(), without specifying any parameter.
get and put stream pointers
All the i/o streams have, at least, one stream pointer:
- ifstream, like istream, has a pointer known as get pointer that points to the next element to be read.
- ofstream, like ostream, has a pointer put pointer that points to the location where the next element has to be written.
- Finally fstream, like iostream, inherits both: get and put
These stream pointers that point to the reading or writing locations within a stream
can be read and/or manipulated using the following member functions:
- tellg() and tellp()
-
These two member functions do not have any parameter and return a value of type
pos_type (according ANSI-C++ standard) that is an integer data type
representing the current position of get stream pointer (in case of tellg)
or put stream pointer (in case of tellp).
- seekg() and seekp()
- This pair of functions serves respectively to change the position of stream pointers
get and put. Both functions are overloaded with two different prototypes:
-
seekg ( pos_type position );
seekp ( pos_type position );
-
This prototype serves to change the stream pointer to an absolute position from the
beginning of the file. The type required is of the same one than the returned by
functions tellg and tellp.
-
seekg ( off_type offset, seekdir direction );
seekp ( off_type offset, seekdir direction );
-
Using this prototype, it can be specified an offset from a concrete point
determined by parameter direction, that can be:
| ios::beg | offset specified from the beginning of the stream |
| ios::cur | offset specified from the current position of the stream pointer |
| ios::end | offset specified from the end of the stream |
It is necessary to indicate that the value of the stream pointers get and put
is counted in different ways for text files than for binary files, since in
text mode files can occur some modifications for the appearance of special characters
that depend on the platform. For that reason it is advisable that with the text files
you use only the first prototype of seekg
and seekp and always using non-modified values returned by
tellg or tellp.
With binary files use freely all the implementations of these functions.
They must not have any unexpected behavior.
The following example uses the member functions just seen to obtain the size of a binary
file:
// obtaining file size
#include <iostream.h>
#include <fstream.h>
const char * filename = "example.txt";
int main () {
long l,m;
ifstream file (filename, ios::in|ios::binary);
l = file.tellg();
file.seekg (0, ios::end);
m = file.tellg();
file.close();
cout << "size of " << filename;
cout << " is " << (m-l) << " bytes.\n";
return 0;
}
|
size of example.txt is 40 bytes.
|
Binary files
In binary files the input and output of data with format like the provided by
the operators << and >> or functions like
getline does not have too much sense (although it is perfectly valid).
File streams include two member functions specially thought for input
and output of data sequentially: write and read.
The first one (write) is a member function of ostream,
also inherited by ofstream. And read is member function of
istream and it is inherited by ifstream.
Objects of class fstream have both. Their prototypes are:
write ( char * buffer, streamsize size );
read ( char * buffer, streamsize size );
Where buffer is the address of a memory block where read data are stored
or where the data to be written is taken from.
The size parameter is an integer value that specifies the number of
characters to be read/written from/to the buffer.
// reading binary file
#include <iostream.h>
#include <fstream.h>
const char * filename = "example.txt";
int main () {
char * buffer;
long size;
ifstream file (filename, ios::in|ios::binary|ios::ate);
size = file.tellg();
file.seekg (0, ios::beg);
buffer = new char [size];
file.read (buffer, size);
file.close();
cout << "the complete file is in a buffer";
delete[] buffer;
return 0;
}
|
the complete file is in a buffer
|
Buffer and Synchronization
When we operate with file streams, these are associated to a buffer
of type streambuf. This buffer is a memory block that
acts as intermediary between the stream and the physical file.
For example, when having an out stream and repeatedly call to member function
put that serves to write a single character, this character is not written
directly to the physical file with which the stream is associated each time
the function is called, instead of that the character is inserted in the
buffer for that stream.
When the buffer is emptied, all data that contains is written to the physic media
(if it is an out stream) or simply erased (if it is an in stream).
This process is called synchronization and it takes place under any of the following
circumstances:
- When the file is closed: before closing a file any buffer that has not been finished to be written or read is synchronized.
- When the buffer is full: Buffers have a certain size. When the buffer is full it is automaticamente synchronized.
- Explicitly with manipulators: When some manipulators are inserted with the insertion operator << a synchronization takes place. These manipulators are: flush and endl.
- Explicitly with function sync(): Calling the member function sync() without parameters causes an immediate syncronization. This function returns an int value equal to -1 if the flow does not have any buffer associated or on failure.
 |
| © The C++ Resources Network, 2000 - All rights reserved |
|