C++ Intro

Sequential Access to Files


Input/Output with files


C++ provides the following classes to perform output and input of characters to/from files:

  • ofstream: Stream class to write on files
  • ifstream: Stream class to read from files
  • fstream: Stream class to both read and write from/to files.

The following example demonstrates the typical use of ofstream:


#include <iostream>
#include <fstream>
using namespace std;

int main () {
  ofstream output_file;
  output_file.open( "filename.txt" );
  output_file << "Hello";
  output_file.close();
  return 0;
}

The above sample creates a file named "filename.txt" and inserts a word "Hello" into it. This code is similar to the way we did output with cout. The output_file represents the file stream object.


Opening a file


The procedure of attaching a file stream to a physical file on disk is called opening the file. The file may or may not previously exist, but opening the file is a required step in either case.

In order to open a file with a file stream object of the C++ Standard Library we invoke member function open( ):


file.open( filename, mode );

Here, filename is a string that contains a directory path and the name of the file, and mode represents a combination of flags classifying operations, which will be performed on the file. The following modes are available:

  • ios::in Open a file for input operations.

  • ios::out Open a file for output operations. Note: (ios::out) and (ios::out|ios::trunc) are identical modes. To avoid truncation, use (ios::in|ios::out) mode.

  • ios::binary Open a file in binary mode.

  • ios::ate Set the initial read/write position at the end of the file as soon as the file gets opened. By default, if this flag is not set to any value, the initial position is set at the beginning of the file. Note: ios::ate works only in read/write mode, that is, requiring fstream and (ios::in|ios::out|ios::ate) mode.

  • ios::app All output operations are performed at the end of the file, effectively appending the content to the current content of the file. This flag can only be used in streams supporting output operations.

  • ios::trunc If the file is opened for output operations and has already existed before, its previous content is deleted and will be replaced by the new data.

The flags can be combined with C++ bitwise OR (a vertical bar | ) operator. For following code:


ofstream myfile;
myfile.open( "example.txt", ios::out | ios::app ); 

Each file stream object comes with a default set of flags that are used in case if the mode is not explicitly provided by the programmer:

  • ofstream -- ios::out
  • ifstream -- ios::in
  • fstream -- ios::in | ios::out

The following outline summarizes all possible modes for file stream operations in C++:

B - ios::binary
I - ios::in
O - ios::out
T - ios::trunc
A - ios::app
Eq - fopen() equivalent (see C-style File Operations for details.)
 
B I O T A Eq    Mode description
--------------  --------------------------------------------------------------
    +     "w"   create text file for writing; discard previous contents if any
    + +   "w"   create text file for writing; discard previous contents if any
    +   + "a"   append; open or create text file for writing at end of file
  + +   + "a+"  read and append; open or create text file for reading anywhere,
                writing at end. All writing occurs at the end of the file,
                protecting previous content from being overwritten. You can
                reposition (fseek, rewind) internal read pointer to anywhere in
                the file for reading, but writing operations will move it back
                to the end of file. The file is created if it does not exist.
  +       "r"   open text file for reading
  + +     "r+"  open text file for update (i.e., reading and writing)
  + + +   "w+"  create text file for update, discard previous contents if any
+   +     "wb"  create binary file for writing; discard previous contents if any
+   + +   "wb"  create binary file for writing; discard previous contents if any
+   +   + "ab"  append; open or create binary file for writing at end of file
+ +       "rb"  open binary file for reading
+ + +     "r+b" open binary file for update (i.e., reading and writing)
+ + + +   "w+b" create binary file for update, discard previous contents if any

Binary files and text files


If ios::binary flag specified, then the file is considered to store a raw (binary) data. Otherwise the file is considered to be a text file, also known as ascii file on operating systems using ASCII encoding for characters. Non-binary files are modified in such way that the line separators CR LF are replaced by single LF character when the information is loaded from the file into computer memory. Consequently, when the file is written back to disk, the line separators are converted back to CR LF characters. This type of processing is called text autoformatting.


Opening file with stream object constructor


An alternative to calling open( ) member function of the file stream is to use the object constructor:


ofstream myfile( "example.txt", ios::out | ios::app ); 

Verifying that file opened correctly


Member function is_open( )  should be invoked to verify that the file has been opened successfully:


// writing on a text file
#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    ofstream myfile( "example.txt" );
    if ( myfile.is_open() ) {
        myfile << "This is a line.\n";
        myfile << "This is another line.\n";
        myfile.close();

    } else {
        cout << "Unable to open file";
    }
    return 0;
}

Again, this example demonstrates that text files are written in exact same manner as the data output sent to the standard output stream device, cout.

Closing a file

Opening a file consumes substantial amount of system resources. Once you are finished using the file, these resources should be freed to become available to the operating system and other programs:


myfile.close();

Once closed, the stream object can be reused for opening another file.

When local file stream object goes out of scope, the file will be closed automatically.

Reading text file

Reading data from a text file is very similar to the data input from the standard input stream, cin:


// reading a text file
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main ()
{
    string line;
    ifstream myfile( "example.txt" );
    if (myfile.is_open()) {
        while ( !myfile.eof() ) {
            // Input line of text:
            getline( myfile, line );
            // Display each line on the screen:
            cout << line << endl;
        }
        myfile.close();

    } else {
        cout << "Unable to open file"; 
    }

    return 0;
}

Checking I/O state

A variety of member functions varify the current condition of the file stream object. These functions are:

  • eof( ) -- returns true if the end of file has been reached.
  • bad( ) -- returns true if a reading or writing operation fails. For example in the case that 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(), but also in the case that a format error happens, like when an alphabetical character is extracted when we are trying to read an integer number.
  • good( ) -- returns true only if each of the above functions return true.

file2string and string2file

To load data from a text file into an STL string, we could use the following sample:


#include <iostream>
#include <fstream>
#include <string>
using namespace std;

// Input data from a text file and store results in a string.
void file2string( char const* filename_, string& str_ )
{
    // on windows platform \r\n are converted into single \n
    ifstream fs( filename_ );
    str_.erase();
    str_.reserve( fs.rdbuf()->in_avail() );
    char ch;
    while( fs.get( ch ) ) {
        if( str_.capacity() == str_.size() )
            str_.reserve( str_.capacity() * 3 );
        str_.append( 1, ch );
    }
}

int main( int argc, char* argv[] )
{
    if ( argc != 2 ) {
        cout << "Please specify a file name to print";
        return 1;
    }
    string input;
    file2string( argv[ 1 ], input );
    cout << input;
    return 0;
}

Next sample demonstrates a reverse operation, which overwrites the content of the file from a string:


#include <iostream>
#include <fstream>
#include <string>
using namespace std;

// Write data from string into a text file.
void string2file( char const* filename_, const string& str_ )
{
    // on windows platform single \n are converted into \r\n
    ofstream fs; 
    fs.open( filename_, ios::trunc );
    fs << str_;
    fs.close(); 
}

int main( int argc, char* argv[] )
{
    if ( argc != 2 ) {
        cout << "Please specify a file name to write";
        return 1;
    }
    string input = "Hello, World!\n";
    string2file( argv[ 1 ], input );
    return 0;
}