MFC Programmer's SourceBook : Thinking in C++
Bruce Eckel's Thinking in C++, 2nd Ed Contents | Prev | Next

File iostreams

Manipulating files with iostreams is much easier and safer than using stdio.h in C. All you do to open a file is create an object; the constructor does the work. You don’t have to explicitly close a file (although you can, using the close( ) member function) because the destructor will close it when the object goes out of scope.

To create a file that defaults to input, make an ifstream object. To create one that defaults to output, make an ofstream object.

Here’s an example that shows many of the features discussed so far. Note the inclusion of <fstream> to declare the file I/O classes; this also includes <iostream>.

//: C18:Strfile.cpp
// Stream I/O with files
// The difference between get() & getline()
#include <fstream>  
#include <iostream>
#include "../require.h"
using namespace std;

int main() {
  const int sz = 100; // Buffer size;
  char buf[sz];
  {
    ifstream in("Strfile.cpp"); // Read
    assure(in, "Strfile.cpp"); // Verify open
    ofstream out("Strfile.out"); // Write
    assure(out, "Strfile.out");
    int i = 1; // Line counter

    // A less-convenient approach for line input:
    while(in.get(buf, sz)) { // Leaves \n in input
      in.get(); // Throw away next character (\n)
      cout << buf << endl; // Must add \n
      // File output just like standard I/O:
      out << i++ << ": " << buf << endl;
    }
  } // Destructors close in & out

  ifstream in("Strfile.out");
  assure(in, "Strfile.out");
  // More convenient line input:
  while(in.getline(buf, sz)) { // Removes \n
    char* cp = buf;
    while(*cp != ':')
      cp++;
    cp += 2; // Past ": "
    cout << cp << endl; // Must still add \n
  }
} ///:~ 

The creation of both the ifstream and ofstream are followed by an assure( ) to guarantee the file has been successfully opened. Here again the object, used in a situation where the compiler expects an integral result, produces a value that indicates success or failure. (To do this, an automatic type conversion member function is called. These are discussed in Chapter 10.)

The first while loop demonstrates the use of two forms of the get( ) function. The first gets characters into a buffer and puts a zero terminator in the buffer when either SZ – 1 characters have been read or the third argument (defaulted to ‘\n’) is encountered. get( ) leaves the terminator character in the input stream, so this terminator must be thrown away via in.get( ) using the form of get( ) with no argument, which fetches a single byte and returns it as an int. You can also use the ignore( ) member function, which has two defaulted arguments. The first is the number of characters to throw away, and defaults to one. The second is the character at which the ignore( ) function quits (after extracting it) and defaults to EOF.

Next you see two output statements that look very similar: one to cout and one to the file out. Notice the convenience here; you don’t need to worry about what kind of object you’re dealing with because the formatting statements work the same with all ostream objects. The first one echoes the line to standard output, and the second writes the line out to the new file and includes a line number.

To demonstrate getline( ), it’s interesting to open the file we just created and strip off the line numbers. To ensure the file is properly closed before opening it to read, you have two choices. You can surround the first part of the program in braces to force the out object out of scope, thus calling the destructor and closing the file, which is done here. You can also call close( ) for both files; if you want, you can even reuse the in object by calling the open( ) member function (you can also create and destroy the object dynamically on the heap as is in Chapter 11).

The second while loop shows how getline( ) removes the terminator character (its third argument, which defaults to ‘\n’) from the input stream when it’s encountered. Although getline( ), like get( ), puts a zero in the buffer, it still doesn’t insert the terminating character.

Open modes

You can control the way a file is opened by changing a default argument. The following table shows the flags that control the mode of the file:

Flag

Function

ios::in

Opens an input file. Use this as an open mode for an ofstream to prevent truncating an existing file.

ios::out

Opens an output file. When used for an ofstream without ios::app, ios::ate or ios::in, ios::trunc is implied.

ios::app

Opens an output file for appending.

ios::ate

Opens an existing file (either input or output) and seeks the end.

ios::nocreate

Opens a file only if it already exists. (Otherwise it fails.)

ios::noreplace

Opens a file only if it does not exist. (Otherwise it fails.)

ios::trunc

Opens a file and deletes the old file, if it already exists.

ios::binary

Opens a file in binary mode. Default is text mode.

These flags can be combined using a bitwise or.

Contents | Prev | Next


Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru