Section 6.1
Input/Output with files

C++ has support both for input and output with files through the following classes:

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. The open file is represented within the program by a stream object (an instantiation of one of these classes) and any input or output performed on this stream object will be applied to the physical file.

In order to open a file with a stream object we use its 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::inOpen file for reading
ios::outOpen file for writing
ios::ateIncial position: end of file
ios::appEvery output is appended at the end of file
ios::truncIf the file already existed it is erased
ios::binaryBinary 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 when opening files that varies from one to other:
classdefault mode to parameter
ofstreamios::out | ios::trunc
ifstreamios::in
fstreamios::in | ios::out
The default value is only applied if the function is called without specifying a mode parameter. If the function is called with any value in that parameter the default mode is stepped on, not combined.

Since the first task that is performed on an object of classes ofstream, ifstream and fstream is frequently to open a file, these three classes include a constructor that calls directly to open member function and that has the same parameters as this. 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 has been correctly associated with an open file or false otherwise.

Closing a file

When reading, writing or consulting operations on a file are complete we must close it so that it become again available. In order to do that we shall call to member function close(), that is in charge of flushing the buffers and closing the file. Its form is quite simple:
void close ();
Once this member function is called, the stream object can be used to open another file, and the file is available again to be opened by other processes.

In case that an object is destructed while still associated with an open file, the destructor automatically calls to member function close.

Text mode files

Classes ofstream, ifstream and fstream are derived from ostream, istream and iostream respectively. That's why fstream objects can use the members of these parent classes to access data.

Generally, when using text files we shall use the same members of these classes that we used in communication with console (cin and cout). 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;
}
file example.txt
This is a line.
This is another line.

Data input from file can also be performed in the same way that we did 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() other member functions to verify the state of the stream exist (all of them return a bool value):
bad()
Returns true if a failure occurs in a reading or writing operation. For example in case we try to write to a file that is not open for writing or if the device where we try to write has no space left.
fail()
Returns true in the same cases as bad() plus in case that a format error happens, as if you try to read an integer number and an alphabetical character is received.
eof()
Returns true if a file opened for reading has reched the end.
good()
It is the most generic: returns false in the same cases in which when calling to any of the previous function true would be returned.
In order to reset the state flags checked by the previous member functions you can use member function clear(), with no parameters.

get and put stream pointers

All i/o streams objects have, at least, one stream pointer:

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 admit no parameters 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 serve 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 );
Using this prototype the stream pointer is changed 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, an offset from a concrete point determined by parameter direction can be specified. It can be:
ios::begoffset specified from the beginning of the stream
ios::curoffset specified from the current position of the stream pointer
ios::endoffset specified from the end of the stream
The values of both stream pointers get and put are counted in different ways for text files than for binary files, since in text mode files some modifications to the appearance of some special characters can occur. For that reason it is advisable to use only the first prototype of seekg and seekp with files opened in text mode and always using non-modified values returned by tellg or tellp. With binary files, you can freely use all the implementations for these functions. They should 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 inputting and outputting data with format, like operators << and >> and functions like getline do, does not have too much sense, although they are 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 are 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

Buffers 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 an intermediary between the stream and the physical file. For example, when having an out stream and repeatedly calls to member function put (write a single character), the 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 flushed, 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:

Previous:
5-5. Preprocessor directives.

index
Next:
6-2. Introduction to STL.
Hosted by www.Geocities.ws

1