Chapter 5: The IO-stream Library

We're always interested in getting feedback. E-mail us if you like this guide, if you think that important material is omitted, if you encounter errors in the code examples or in the documentation, if you find any typos, or generally just if you feel like e-mailing. Send your email to Frank Brokken.

Please state the document version you're referring to, as found in the title (in this document: 5.2.0a) and please state the paragraph you're referring to.

All mail received is seriously considered, and new (sub)releases of the Annotations will normally reflect your suggestions for improvements. Except for the incidental case I will not otherwise acknowledge the receipt of suggestions for improvements. Please don't misinterpret this for lack of appreciation.

As an extension to the standard stream ( FILE) approach well known from the C programming language, C++ offers an input/output ( I/O) library based on class concepts.

Earlier (in chapter 3) we've already seen examples of the use of the C++ I/O library, especially the use of the insertion operator (operator<<()) and the extraction operator (operator>>()). In this chapter we'll cover the library in more detail.

The discussion of input and output facilities provided by the C++ programming language heavily uses the class concept, and the notion of member functions. Although the construction of classes will be covered in the upcoming chapter 6, and inheritance only in chapter 13, we think it is well possible to introduce input and output (I/O) facilities well before discussing the technical background of these topics.

Most C++ I/O classes have names starting with basic_ (like basic_ios). However, these basic_ names are not regularly found in C++ programs, as most classes are also defined using typedef definitions like:

typedef basic_ios<char> ios;

Since C++ defines both the char and wchar_t types, I/O facilities were developed using the template mechanism. As will be further elaborated in chapter 18, this way it was possible to construct generic software, which could be used for both the char and wchar_t types. So, analogously to the above typedef there exists a

typedef basic_ios<wchar_t> wios;

This type definition can be used for the wchar_t type. Due to the type definitions, the basic_ prefix can be omitted in the Annotations. In the Annotations the emphasis is primarily on the standard 8-bits char type.

As a side effect to this implementation it must be stressed that it is not correct anymore to declare iostream objects using standard forward declarations, like:

    class ostream;          // now erroneous
Instead, sources that must declare iostream classes must
    #include <iosfwd>       // correct way to declare iostream classes

Using the C++ I/O library offers the additional advantage of type safety. Objects (or plain values) are inserted into streams. Compare this to the situation commonly encountered in C where the fprintf() function is used to indicate by a format string what kind of value to expect where. Compared to this latter situation C++'s iostream approach immediately uses the objects where their values should appear, as in

cout << "There were " << nMaidens << " virgins present\n";

The compiler notices the type of the nMaidens variable, inserting its proper value at the appropriate place in the sentence inserted into the cout iostream.

Compare this to the situation encountered in C. Although C compilers are getting smarter and smarter over the years, and although a well-designed C compiler may warn you for a mismatch between a format specifier and the type of a variable encountered in the corresponding position of the argument list of a printf() statement, it can't do much more than warn you. The type safety seen in C++ prevents you from making type mismatches, as there are no types to match.

Apart from this, iostreams offer more or less the same set of possibilities as the standard FILE-based I/O used in C: files can be opened, closed, positioned, read, written, etc.. In C++ the basic FILE structure, as used in C is still available. C++ adds I/O based on classes to FILE-based I/O, resulting in type safety, extensibility, and a clean design. In the ANSI/ISO standard the intent was to construct architecture independent I/O. Previous implementations of the iostreams library did not always comply with the standard, resulting in many extensions to the standard. Software developed earlier may have to be partially rewritten with respect to I/O. This is tough for those who are now forced to modify existing software, but every feature and extension that was available in previous implementations can be reconstructed easily using the ANSI/ISO standard conforming I/O library. Not all of these reimplementations can be covered in this chapter, as most use inheritance and polymorphism, topics that will be covered in chapters 13 and 14, respectively. Selected reimplementations will be provided in chapter 19, and below references to particular sections in that chapter will be given where appropriate.

figure 3 is shown here.
figure 3: Central I/O Classes


This chapter is organized as follows (see also figure 3):

In the iostream library the stream objects have a limited role: they form the interface between, on the one hand, the objects to be input or output and, on the other hand, the streambuf, which is responsible for the actual input and output to the device for which the streambuf object was created in the first place. This approach allows us to construct a new kind of streambuf for a new kind of device, and use that streambuf in combination with the `good old' istream- or ostream-class facilities. It is important to understand the distinction between the formatting roles of the iostream objects and the buffering interface to an external device as implemented in a streambuf. Interfacing to new devices (like sockets or file descriptors) requires us to construct a new kind of streambuf, not a new kind of istream or ostream object. A wrapper class may be constructed around the istream or ostream classes, though, to ease the access to a special device. This is how the stringstream classes were constructed.

5.1: Iostream header files

Several header files are defined for the iostream library. Depending on the situation at hand, the following header files should be used:

5.2: The foundation: the class `ios_base'

The class ios_base forms the foundation of all I/O operations, and defines, among other things, the facilities for inspecting the state of I/O streams and most output formatting facilities. Every stream class of the I/O library is, via the class ios, derived from this class, and inherits its capabilities.

The discussion of the class ios_base precedes the introduction of members that can be used for actual reading from and writing to streams. But as the ios_base class is the foundation on which all I/O in C++ was built, we introduce it as the first class of the C++ I/O library.

Note, however, that as in C, I/O in C++ is not part of the language (although it is part of the ANSI/ISO standard on C++): although it is technically possible to ignore all predefined I/O facilities, nobody actually does so, and the I/O library represents therefore a de facto I/O standard in C++. Also note that, as mentioned before, the iostream classes do not do input and output themselves, but delegate this to an auxiliary class: the class streambuf or its derivatives.

For the sake of completeness and not so much because it is necessary to understand the ongoing discussion, it is noted here that it is not possible to construct an ios_base object directly. As covered by chapter 13, classes that are derived from ios_base (like ios) may construct ios_base objects using the ios_base::ios_base() constructor.

The next class in the iostream hierarchy (see figure 3) is the class ios. Since the stream classes inherit from the class ios, and thus also from ios_base, in practice the distinction between ios_base and ios is hardly important. Therefore, in the sequel facilities actually provided by ios_base are discussed as facilities provided by ios. The reader who is interested in the true class in which a particular facility is defined should consult the relevant header files (e.g., ios_base.h and basic_ios.h).

5.3: Interfacing `streambuf' objects: the class `ios'

The ios class is directly derived from ios_base, and defines de facto the foundation for all stream classes of the C++ I/O library.

It is noted that, although it is possible to construct an ios object directly, this is hardly ever done. The purpose of the class ios is to provide the facilities of the class basic_ios, and add to this several new facilites, all related to managing the streambuf object which is managed by objects of the class ios.

All other stream classes are either directly or indirectly derived from ios. This implies, as explained in chapter 13, that all facilities offered by the classes ios and ios_base are also available in the stream classes. Before discussing these stream classes, the facilities offered by the classes ios_base and ios will now be introduced, pretending they are all facilities provided by the class ios.

The class offers several member functions, most of which are related to formatting. Other frequently used member functions are:

5.3.1: Condition states

Operations on streams may succeed and they may fail for several reasons. Whenever an operation fails, further read and write operations on the stream are suspended. It is possible to inspect (and possibly: clear) the condition state of streams, so that a program can repair the problem, instead of having to abort.

Conditions are represented by the following condition flags:

Several condition member functions are available to manipulate or determine the states of ios objects:

Ios objects can also be interrogated using boolean operators: true is returned in boolean expressions for ios objects if ios::good() would have returned a non-zero value. So, a construction like

    cin >> x;   // cin is also an ios object

    if (cin)
        cout << "Good extraction of `x'\n";
is possible. Also, the complementary test is possible:
    cin >> x;   // cin is also an ios object

    if (!cin)
        cout << "Extraction of `x' failed\n";
Once an error condition has been raised (ios::goodbit has not been set), further processing of the ios object is suspended.

The following members are available for the mangement of error states:

C++ supports an exception mechanism for handling exceptional situations. According to the ANSI/ISO standard, exceptions can be used with stream objects. Exceptions are covered in chapter 8. Using exceptions with stream objects is covered in section 8.7.

5.3.2: Formatting output and input

The way information is written to streams (or, occasionally, read from streams) may be controlled by formatting flags.

Formatting may involve the control of the width of an output field or an input buffer or the form (e.g., the radix) in which a value is displayed. Most of the format control is realized in the context of the ios class, although most formatting is actually done with output streams, like the upcoming ostream class. Since the formatting is controlled by flags, which are defined in the ios class, it was considered best to discuss formatting with the ios class itself, rather than with a selected derived class, where the choice of the derived class would always be somewhat arbitrarily.

The flags control the formatting, but the flags can be altered basically in two ways: using specialized member functions, discussed in section 5.3.2.2 or using manipulators, which are directly inserted into streams. Manipulators are not applied directly to the ios class, as they require the use of the insertion operator. Consequently they are discussed later (in section 5.6).

5.3.2.1: Formatting flags

Most formatting flags are related to outputting information. Information can be written to output streams in basically two ways: binary output will write information directly to the output stream, without conversion to some human-readable format. E.g., an int value is written as a set of four bytes. Alternatively, formatted output will convert the values that are stored in bytes in the computer's memory to ASCII-characters, in order to create a human-readable form.

Formatting flags can be used to define the way this conversion takes place, to control, e.g., the number of characters that are written to the output stream.

The following formatting flags are available (see also sections 5.3.2.2 and 5.6):

5.3.2.2: Format modifying member functions

Several member functions are available for I/O formatting. Often, corresponding manipulators exist, which may directly be inserted into or extracted from streams using insertion or extraction operators. See section 5.6 for a discussion of the available manipulators. They are:

5.4: Output

Output in C++ evolves around the ostream class. The ostream class defines the basic operators and members for inserting information into streams: the insertion operator ( operator<<()), and special members like ostream::write() for writing unformatted information from streams.

From the class ostream several other classes are derived, all having the functionality of the ostream class, and adding their own specialties. In the next sections on `output' we will introduce:

5.4.1: Basic output: the class `ostream'

The class ostream is the class defining basic output facilities. The cout, clog and cerr objects are all ostream objects. Note that all facilities defined in the ios class, as far as output is concerned, is available in the ostream class as well, due to the inheritance mechanism (discussed in chapter 13).

Ostream objects can be constructed using the following ostream constructor:

In order to use the ostream class in C++ sources, the #include <ostream> preprocessor directive must be given. To use the predefined ostream objects, the #include <iostream> preprocessor directive must be given.

5.4.1.1: Writing to `ostream' objects

The class ostream supports both formatted and binary output.

The insertion operator ( operator<<()) may be used to insert values in a type safe way into ostream objects. This is called formatted output, as binary values which are stored in the computer's memory are converted to human-readable ASCII characters according to certain formatting rules.

Note that the insertion operator points to the ostream object wherein the information must be inserted. The normal associativity of operator<<() remains unaltered, so when a statement like

cout << "hello " << "world";

is encountered, the leftmost two operands are evaluated first (cout << "hello "), and an ostream & object, which is actually the same cout object, is returned. Now, the statement is reduced to

cout << "world"

and the second string is inserted into cout.

The << operator has a lot of (overloaded) variants, so many types of variables can be inserted into ostream objects. There is an overloaded <<-operator expecting an int, a double, a pointer, etc. etc.. For every part of the information that is inserted into the stream the operator returns the ostream object into which the information so far was inserted, and the next part of the information to be inserted is devoured.

Streams do not have facilities for formatted output like C's form() and vform() functions. However, it is not difficult to make these facilities available in the world of streams. In section 19.2 the construction of a class defining a form() member function is illustrated.

When binary files must be written, the information should normally not be formatted: an int value should be written as a series of unaltered bytes, not as a series of ASCII numeric characters 0 to 9. The following member functions are defined for ostream objects:

5.4.1.2: `ostream' positioning

Although not every ostream object supports repositioning, they usually do. This means that it is possible to rewrite a section of the stream which was written earlier. Repositioning is frequently used in database applications where it must be possible to access the information in the database randomly.

The following members are available:

5.4.1.3: `ostream' flushing

Unless the ios::unitbuf flag has been set, information written to an ostream object is not immediately written to the physical stream. Rather, an internal buffer is filled up during the write-operations, and when full it is flushed.

The internal buffer can be flushed under program control:

5.4.2: Output to files: the class `ofstream'

The ofstream class is derived from the ostream class: it has the same capabilities as the ostream class, but can be used to access files or create files for writing.

In order to use the ofstream class in C++ sources, the preprocessor directive #include <fstream> must be given. After including fstream cin, cout etc. are not automatically declared. If these latter objects are needed too, then iostream should be included.

The following constructors are available for ofstream objects:

Note that it is not possible to open a ofstream using a file descriptor. The reason for this is (apparently) that file descriptors are not universally available over different operating systems. Fortunately, file descriptors can be used (indirectly) with a streambuf object (and in some implementations: with a filebuf object, which is also a streambuf). Streambuf objects are discussed in section 5.7, filebuf objects are discussed in section 5.7.2.

Instead of directly associating an ofstream object with a file, the object can be constructed first, and opened later.

A subtlety is the following: Assume a stream is constructed, but it is not actually attached to a file. E.g., the statement ofstream ostr was executed. When we now check its status through good(), a non-zero (i.e., ok) value will be returned. The `good' status here indicates that the stream object has been properly constructed. It doesn't mean the file is also open. To test whether a stream is actually open, inspect ofstream::is_open(): If true, the stream is open. See the following example:
    #include <fstream>
    #include <iostream>

    using namespace std;

    int main()
    {
        ofstream
            of;

        cout << "of's open state: " << boolalpha << of.is_open() << endl;

        of.open("/dev/null");       // on Unix systems
        
        cout << "of's open state: " << of.is_open() << endl;
    }
    /*
        Generated output:
    of's open state: false
    of's open state: true
    */

5.4.2.1: Modes for opening stream objects

The following file modes or file flags are defined for constructing or opening ofstream (or istream, see section 5.5.2) objects. The values are of type ios::openmode:

The following combinations of file flags have special meanings:
    out | app:          The file is created if non-existing, 
                        information is always added to the end of the 
                        stream;
    out | trunc:        The file is (re)created empty to be written;
    in | out:           The stream may be read and written. However, the 
                        file must exist.
    in | out | trunc:   The stream may be read and written. It is 
                        (re)created empty first.

5.4.3: Output to memory: the class `ostringstream'

In order to write information to memory, using the stream facilities, ostringstream objects can be used. These objects are derived from ostream objects. The following constructors and members are available: Before the stringstream class was available the class ostrstream was commonly used for doing output to memory. This latter class suffered from the fact that, once its contents were retrieved using its str() member function, these contents were `frozen', meaning that its dynamically allocated memory was not released when the object went out of scope. Although this situation could be prevented (using the ostrstream member call freeze(0)), this implementation could easily lead to memory leaks. The stringstream class does not suffer from these risks. Therefore, the use of the class ostrstream is now deprecated in favor of ostringstream and ostrstream objects should be avoided.

The following example illustrates the use of the ostringstream class: several values are inserted into the object. Then, the stored text is stored in a string, whose length and contents are thereupon printed. Ostringstream objects are most often used for doing `type to string' conversions, like converting int to string. Formatting commands can be used with stringstreams as well, as they are available in ostream objects. It should also be possible to perform seek operations with ostringstream objects, but the current Gnu g++ (version 3.0) implementation apparently does not support them. Here is an example showing the use of an ostringstream object:

    #include <iostream>
    #include <string>
    #include <sstream>
    #include <fstream>
    
    using namespace std;
    
    int main()
    {
        ostringstream
            ostr("hello ", ios::ate);
    
        cout << ostr.str() << endl;

        ostr.setf(ios::showbase);
        ostr.setf(ios::hex, ios::basefield);
        ostr << 12345;
    
        cout << ostr.str() << endl;

        ostr << " -- ";
        ostr.unsetf(ios::hex);
        ostr << 12;
    
        cout << ostr.str() << endl;
    }
    /*
        Output from this program:
    hello 
    hello 0x3039
    hello 0x3039 -- 12
    */

5.5: Input

Input in C++ evolves around the istream class. The istream class defines the basic operators and members for extracting information from streams: the extraction operator ( operator>>()), and special members like istream::read() for reading unformatted information from streams.

From the class istream several other classes are derived, all having the functionality of the istream class, and adding their own specialties. In the next sections we will introduce:

5.5.1: Basic input: the class `istream'

The class istream is the I/O class defining basic input facilities. The cin object is an istream object that is declared when sources contain the preprocessor directive #include <iostream>. Note that all facilities defined in the ios class are, as far as input is concerned, available in the istream class as well due to the inheritance mechanism (discussed in chapter 13).

Istream objects can be constructed using the following istream constructor:

In order to use the istream class in C++ sources, the #include <istream> preprocessor directive must be given. To use the predefined istream object cin, the #include <iostream> preprocessor directive must be given.

5.5.1.1: Reading from `istream' objects

The class istream supports both formatted and unformatted binary input. The extraction operator ( operator>>()) may be used to extract values in a type safe way from istream objects. This is called formatted input, whereby human-readable ASCII characters are converted, according to certain formatting rules, to binary values which are stored in the computer's memory.

Note that the extraction operator points to the objects or variables which must receive new values. The normal associativity of operator>>() remains unaltered, so when a statement like

cin >> x >> y;

is encountered, the leftmost two operands are evaluated first (cin >> x), and an istream & object, which is actually the same cin object, is returned. Now, the statement is reduced to

cin >> y

and the y variable is extracted from cin.

The >> operator has a lot of (overloaded) variants, so many types of variables can be extracted from istream objects. There is an overloaded operator>>() available for the extraction of an int, of a double, of a string, of an array of characters, possibly to a pointer, etc. etc.. String or character array extraction will (by default) skip all white space characters, and will then extract all consecutive non-white space characters. After processing an extraction operator, the istream object into which the information so far was inserted is returned, which will thereupon be used as the lvalue for the remaining part of the statement.

Streams do not have facilities for formatted output like C's scanf() and vscanf() functions. However, it is not difficult to make these facilities available in the world of streams. In section 19.2 the construction of a class defining a form() member function is illustrated. The construction of a comparable iscanstream class may proceed analogously.

When binary files must be read, the information should normally not be formatted: an int value should be read as a series of unaltered bytes, not as a series of ASCII numeric characters 0 to 9. The following member functions for reading information from istream objects are available:

5.5.1.2: `istream' positioning

Although not every istream object supports repositioning, some do. This means that it is possible to rewrite a section of the stream which was written earlier. Repositioning is frequently used in database applications where it must be possible to access the information in the database randomly.

The following members are available:

5.5.2: Input from streams: the class `ifstream'

The class ifstream is derived from the class istream: it has the same capabilities as the istream class, but can be used to access files for reading. Such files must exist.

In order to use the ifstream class in C++ sources, the preprocessor directive #include <fstream> must be given.

The following constructors are available for ifstream objects:

Instead of directly associating an ifstream object with a file, the object can be constructed first, and opened later. A subtlety is the following: Assume a stream is constructed, but it is not actually attached to a file. E.g., the statement ifstream ostr was executed. When we now check its status through good(), a non-zero (i.e., ok) value will be returned. The `good' status here indicates that the stream object has been properly constructed. It doesn't mean the file is also open. To test whether a stream is actually open, inspect ifstream::is_open(): If true, the stream is open. See also the example in section 5.4.2.

To illustrate reading from a binary file (see also section 5.5.1.1), a double value is read in binary form from a file in the next example:

    #include <fstream>
    using namespace std;

    int main(int argc, char **argv)
    {
        ifstream
            f(argv[1]);
        double
            d;

        // reads double in binary form.
        f.read(reinterpret_cast<char *>(&d), sizeof(double));  
    }

5.5.3: Input from memory: the class `istringstream'

In order to read information from memory, using the stream facilities, istringstream objects can be used. These objects are derived from istream objects. The following constructors and members are available:

The istringstream object is commonly used for converting ASCII text to its binary equivalent, like the C function atoi(). The following example illustrates the use of the istringstream class, note especially the use of the member seekg():

    #include <iostream>
    #include <string>
    #include <sstream>
    
    using namespace std;
    
    int main()
    {
        istringstream
            istr("123 345");        // store some text.
        int
            x;

        istr.seekg(2);              // skip "12"
        istr >> x;                  // extract int
        cout << x << endl;          // write it out
        istr.seekg(0);              // retry from the beginning
        istr >> x;                  // extract int
        cout << x << endl;          // write it out
        istr.str("666");            // store another text
        istr >> x;                  // extract it
        cout << x << endl;          // write it out
    }
    /*
        output of this program:
    3
    123
    666
    */

5.6: Manipulators

Ios objects define a set of format flags that are used for determining the way values are inserted (see section 5.3.2.1). The format flags can be controlled by member functions (see section 5.3.2.2), but also by manipulators. Manipulators are inserted into output streams or extracted from input streams, instead of being activated through the member selection operator (`.').

Manipulators are functions. New manipulators can be constructed as well. The construction of manipulators is covered in section 9.9.1. In this section the manipulators that are available in the C++ I/O library are discussed. Most manipulators affect format flags. See section 5.3.2.1 for details about these flags. Most manipulators are parameterless. Sources in which manipulators expecting arguments are used, must do:

#include <iomanip>

5.7: The `streambuf' class

The class streambuf defines the input and output character sequences that are processed by streams. Like an ios object, a streambuf object is not directly constructed, but is implied by objects of other classes that are specializations of the class streambuf.

The class has an important role in realizing possibilities that were available as extensions to the pre- ANSI/ISO standard implementations of C++. Although the class cannot be used directly, its members are introduced here, as the current chapter is the most logical place to introduce the class streambuf.

The primary reason for existence of the class streambuf, however, is to decouple the stream classes from the devices they operate upon. The rationale here is to use an extra software layer between on the one hand the classes allowing us to communicate with the device and the communication between the software and the devices themselves. This implements a chain of command which is seen regularly in software design: The chain of command is considered a generic pattern for the construction of reusable software, encountered also in, e.g., the TCP/IP stack. A streambuf can be considered yet another example of the chain of command pattern: here the program talks to stream objects, which in turn forward their requests to streambuffer objects, which in turn communicate with the devices. Thus, as we will see shortly, we are now able to do in user-software what had to be done via (expensive) system calls before.

The class streambuf has no public constructor, but does make available several public member functions. In addition to these public member functions, several member functions are available to specializing classes only. These protected members are listed in this section for further reference. In section 5.7.2 below, a particular specialization of the class streambuf is introduced. Note that all public members of streambuf discussed here are also available in filebuf.

In section 14.6 the process of constructing specializations of the class streambuf is discussed, and in chapter 19 several other implications of using streambuf objects are mentioned. In the current chapter examples of copying streams, of redirecting streams and and of reading and writing to streams using the streambuf members of stream objects are presented (section 5.8).

With the class streambuf the following public member functions are available. The type streamsize that is used below may, for all practical purposes, be considered an unsigned int.

Public members for input operations:

Public members for output operations:

Public members for miscellaneous operations:

5.7.1: Protected `streambuf' members

The protected members of the class streambuf are normally not accessible. However, they are accessible in specializing classes which are derived from streambuf. They are important for understanding and using the class streambuf. Usually there are both protected data members and protected member functions defined in the class streambuf. Since using data members immediately violates the principle of encapsulation, these members are not mentioned here. As the functionality of streambuf, made available via its member functions, is quite extensive, directly using its data members is probably hardly ever necessary. This section not even lists all protected member functions of the class streambuf. Only those member functions are mentioned that are useful in constructing specializations. The class streambuf maintains an input- and/or and output buffer, for which beginning, actual and ending pointers are defined, as depicted in figure 4. In the sequel we will refer to this figure repeatedly.

figure 4 is shown here.
figure 4: Input- and output buffer pointers of the class `streambuf'


Protected constructor:

Protected member functions related to input operations:

Protected member functions related to output operations:

Protected member functions related to buffer management and positioning:

Morale: when specializations of the class streambuf are designed, the very least thing to do is to redefine underflow() for specializations aimed at reading information from devices, and to redefine overflow() for specializations aimed at writing information to devices. Several examples of specializations of the class streambuf will be given in the sequel.

5.7.2: The class `filebuf'

The class filebuf is a specialization of streambuf used by the file stream classes. Apart from the (public) members that are available through the class streambuf, it defines the following extra (public) members:

5.8: Advanced topics

5.8.1: Copying streams

Usually, files are copied either by reading a source file character by character or line by line. The basic mold for processing files is as follows: It is important to note that the reading must precede the testing, as it is only possible to know after the actual attempt to read from a file whether the reading succeeded or not. Of course, variations are possible: getline(istream &, string &) (see section 5.5.1.1) returns an istream & itself, so here reading and testing may be realized in one expression. Nevertheless, the above mold represents the general case. So, the following program could be used to copy cin to cout:
#include <iostream>

using namespace::std;

int main()
{
    while (true)
    {
        char c;

        cin.get(c);
        if (cin.fail())
            break;
        cout << c;
    }
    return 0;
}

By combining the get() with the if-statement a construction comparable to getline() could be used:

    if (!cin.get(c)) 
        break;
Note, however, that this would still follow the basic rule: ` read first, test later'.

This simple copying of a file, however, isn't required very often. More often, a situation is encountered where a file is processed up to a certain point, whereafter the remainder of the file can be copied unaltered. The following program illustrates this situation: the ignore() call is used to skip the first line (for the sake of the example it is assumed that the first line is at most 80 characters long), the second statement used a special overloaded version of the <<-operator, in which a streambuf pointer is inserted into another stream. As the member rdbuf() returns a streambuf *, it can thereupon be inserted into cout. This immediately copies the remainder of cin to cout:

    #include <iostream>
    using namespace std;
    
    int main()
    {
        cin.ignore(80, '\n');   // skip the first line
        cout << cin.rdbuf();    // copy the rest by inserting a streambuf *
    }
Note that this method assumes a streambuf object, so it will work for all specializations of streambuf. Consequently, if the class streambuf is specialized for a particular device it can be inserted into any other stream using the above method.

5.8.2: Coupling streams

Ostreams can be coupled to ios objects using the tie() member function. This results in flushing any buffered output of the ostream object (by calling flush()) when any input or output is performed on the ios object to which the ostream object is tied. By default cout is tied to cout. To break coupling, the member function ios::tie(0) can be called.

Another (frequently useful) example of coupling streams is to tie cerr to cout: this way standard output and error messages written to the screen will appear in sync with the time at which they were generated:

    #include <iostream>
    using namespace std;
    
    int main()
    {
        cout << "first (buffered) line to cout\n";
        cerr << "first (unbuffered) line to cerr\n";

        cerr.tie(&cout);

        cout << "second (buffered) line to cout\n";
        cerr << "second (unbuffered) line to cerr\n";
    }
    /*
        Generated output:
    first (unbuffered) line to cerr
    first (buffered) line to cout
    second (buffered) line to cout
    second (unbuffered) line to cerr
    */

An alternative way to couple streams is to make streams use a common streambuf object. This can be realized using the ios::rdbuf(streambuf *) member function. This way two streams can use, e.g. their own formatting, one stream can be used for input, the other for output, and redirection using the iostream library rather than operating system calls can be realized. See the next sections for examples.

5.8.3: Redirection using streams

By using the ios::rdbuf() member streams can share their streambuf objects. This means that the information that is written to a stream will actually be written to another stream, a phenomenon normally called redirection. Redirection is normally realized at the level of the operating system, and in some situations that is still necessary (see section 19.3).

A standard situation where redirection is wanted is to write error messages to file rather than to the standard error file, usually indicated by its file descriptor number 2. In the Unix operating system using the bash shell, this can be realized as follows:

    program 2>/tmp/error.log
With this command any error messages written by program will be saved on the file /tmp/error.log, rather than being written to the screen.

Here is how this can be realized using streambuf objects. Assume program now expects an optional argument defining the name of the file to write the error messages to; so program is now called as:

    program /tmp/error.log
Here is the example realizing redirection. It is annotated below.
    #include <iostream>
    #include <fstream>

    using namespace std;

    int main(int argc, char **argv)
    {
        ofstream                                        // 8
            errlog;
        streambuf
            *cerr_buffer = 0;                           // 11

        if (argc == 2)
        {
            errlog.open(argv[1]);                       // 15
            cerr_buffer = cerr.rdbuf(errlog.rdbuf());   // 16
        }
        else
        {
            cerr << "Missing log filename\n";
            return 1;
        }

        cerr << "Several messages to stderr, msg 1\n";
        cerr << "Several messages to stderr, msg 2\n";

        cout << "Now inspect the contents of " <<
                argv[1] << "... [Enter] ";
        cin.get();                                      // 29

        cerr << "Several messages to stderr, msg 3\n";

        cerr.rdbuf(cerr_buffer);                        // 33
        cerr << "Done\n";                               // 34
    }
    /*
        Generated output on file argv[1]

        at cin.get():
    
    Several messages to stderr, msg 1
    Several messages to stderr, msg 2

        at the end of the program:

    Several messages to stderr, msg 1
    Several messages to stderr, msg 2
    Several messages to stderr, msg 3
    */

5.8.4: Reading AND Writing to a stream

In order to be able to read and write to a stream an fstream object must be created. As with ifstream and ofstream objects, the constructor receives the name of the file to be opened:
fstream inout("iofile", ios::in | ios::out);

Note the use of the ios constants ios::in and ios::out, indicating that the file must be opened for both reading and writing. Multiple mode indicators may be used, concatenated by the binary or operator '|'. Alternatively, instead of ios::out, ios::app could have been used, in which case writing will always be done at the end of the file.

Somehow reading and writing to a file is a bit awkward: what to do when the file may or may not exist yet, but if it already exists it should not be rewritten? I have been fighting with this problem for some time, and now use the following approach:

    #include <fstream>
    #include <iostream>
    #include <string>

    using namespace std;

    int main()
    {
        fstream rw("fname", ios::out | ios::ate | ios::in); 
        if (!rw)
        {
            rw.clear();
            rw.open("fname", ios::out | ios::trunc | ios::in); 
        }
        if (!rw)
        {
            cerr << "Opening `fname' failed miserably" << endl;
            return 1;
        }

        cerr << rw.tellp() << endl;

        rw << "Hello world" << endl;
        rw.seekg(0);

        string s;
        getline(rw, s);

        cout << "Read: " << s << endl;
    }
In the above example, the constructor fails when fname doesn't exist yet. However, in that case the open() member succeeds. If the file already exists, the constructor succeeds. Because of the ios::ate flag, the next read/write action will by default take place at EOF. However, ios::ate is not ios::app, and the stream object can be repositioned using seekg() or seekp().

Under DOS-like operating systems, which use the multiple character \r\n sentinels to separate lines in text files the flag ios::bin is required for processing binary files to ensure that \r\n combinations are processed as two characters.

With fstream objects, combinations of file flags are used to make sure that a stream is or is not (re)created empty when opened. See section 5.4.2.1 for details.

Once a file has been opened in read and write mode, the << operator can be used to insert information to the file, while the >> operator may be used to extract information from the file. These operations may be performed in random order. The following fragment will read a blank-delimited word from the file, and will then write a string to the file, just beyond the point where the string just read terminated, followed by the reading of yet another string just beyond the location where the string just written ended:

    fstream
        f("filename", ios::in | ios::out | ios::creat);
    string
        str;
        
    f >> str;       // read the first word
                    // write a well known text
    f << "hello world";
    f >> str;       // and read again
Since the operators << and >> can apparently be used with fstream objects, you might wonder whether a series of << and >> operators in one statement might be possible. After all, f >> str should produce an fstream &, shouldn't it?

The answer is: it doesn't. The compiler casts the fstream object into an ifstream object in combination with the extraction operator, and into an ofstream object in combination with the insertion operator. Consequently, a statement like

f >> str << "grandpa" >> str;

results in a compiler error like

no match for `operator <<(class istream, char[8])'

Since the compiler complains about the istream class, the fstream object is apparently considered an ifstream object in combination with the extraction operator.

Of course, random insertions and extractions are hardly used. Generally, insertions and extractions take place at specific locations in the file. In those cases, the position where the insertion or extraction must take place can be controlled and monitored by the seekg() and tellg() member functions (see sections 5.4.1.2 and 5.5.1.2).

Error conditions (see section 5.3.1) occurring due to, e.g., reading beyond end of file, reaching end of file, or positioning before begin of file, can be cleared using the clear() member function. Following clear() processing may continue. E.g.,

    fstream
        f("filename", ios::in | ios::out | ios::creat);
    string
        str;

    f.seekg(-10);   // this fails, but...
    f.clear();      // processing f continues

    f >> str;       // read the first word
A common situation in which files are both read and written occurs in data base applications, where files consists of records of fixed size, or where the location and size of pieces of information is well known. For example, the following program may be used to add lines of text to a (possibly existing) file, and to retrieve a certain line, based on its order-numer from the file. Note the use of the binary file index to retrieve the location of the first byte of a line.
    #include <iostream>
    #include <fstream>
    #include <string>
    
    using namespace std;

    void err(char const *msg)
    {
        cout << msg << endl;
        return;
    }
    
    void err(char const *msg, long value)
    {
        cout << msg << value << endl;
        return;
    }
    
    void read(fstream &index, fstream &strings)
    {
        int 
            idx;
    
        if (!(cin >> idx))                          // read index
            return err("line number expected");
    
        index.seekg(idx * sizeof(long));            // go to index-offset
    
        long
            offset;
    
        if 
        (
            !index.read                             // read the line-offset
            (
                reinterpret_cast<char *>(&offset), 
                sizeof(long)
            )            
        )
            return err("no offset for line", idx);  
    
        if (!strings.seekg(offset))                 // go to the line's offset
            return err("can't get string offet ", offset);
            
        string
            line;
        
        if (!getline(strings, line))                // read the line
            return err("no line at ", offset);
    
        cout << "Got line: " << line << endl;       // show the line
    }
    
    
    void write(fstream &index, fstream &strings)
    {
        string
            line;
    
        if (!getline(cin, line))                  // read the line
            return err("line missing");
    
        strings.seekp(0, ios::end);               // to strings 
        index.seekp(0, ios::end);                 // to index 
    
        long
            offset = strings.tellp();   
    
        if 
        (
            !index.write                          // write the offset to index
            (
                reinterpret_cast<char *>(&offset), 
                sizeof(long)
            )
        )
            err("Writing failed to index: ", offset);
    
        if (!(strings << line << endl))           // write the line itself
            err("Writing to `strings' failed");
                                                  // confirm writing the line
        cout << "Write at offset " << offset << " line: " << line << endl;
    }
        
    int main()
    {
        fstream
            index("index", ios::trunc | ios::in | ios::out),
            strings("strings", ios::trunc | ios::in | ios::out); 

        cout << "enter `r <number>' to read line <number> or "
                                    "w <line>' to write a line\n"
                "or enter `q' to quit.\n";
    
        while (true)
        {
            cout << "r <nr>, w <line>, q ? ";       // show prompt
            string
                cmd;
    
            cin >> cmd;                             // read cmd
    
            if (cmd == "q")                         // process the cmd.
                return 0;
    
            if (cmd == "r")
                read(index, strings);
            else if (cmd == "w")
                write(index, strings);
            else
                cout << "Unknown command: " << cmd << endl;
        }
    }
As another example of reading and writing files, consider the following program, which also serves as an illustration of reading an ASCII-Z delimited string:
    #include <iostream>
    #include <fstream>
    using namespace std;

    int main()
    {
        fstream                             // r/w the file
            f("hello", ios::in | ios::out | ios::trunc); 

        f.write("hello", 6);                // write 2 ascii-z 
        f.write("hello", 6);
    
        f.seekg(0, ios::beg);               // reset to begin of file
    
        char 
            buffer[20],
            c;
                                            // read the first `hello'
        cout << f.get(buffer, 100, 0).tellg() << endl;;
        f >> c;                             // read the ascii-z delim

                                            // and read the second `hello'
        cout << f.get(buffer + 6, 100, 0).tellg() << endl;

        buffer[5] = ' ';                    // change asciiz to ' '
        cout << buffer << endl;             // show 2 times `hello'
    }
    /*
        Generated output:
    5
    11
    hello hello
    */

By using the streambuf members of stream objects a completely different way to both read and write to streams can be implemented. All mentioned considerations remain valid: before a read operation following a write operation a seekg() must be used, and before a write operating following a read operation a seekp() must be used. When the stream's streambuf objects are used, either an istream is associated with the streambuf object of another ostream object, or vice versa, an ostream object is associated with the streambuf object of another istream object. Here is the same program as before, now using associated streams:

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

    void err(char const *msg)
    {
        cout << msg << endl;
        return;
    }
    
    void err(char const *msg, long value)
    {
        cout << msg << value << endl;
        return;
    }
    
    void read(istream &index, istream &strings)
    {
        int 
            idx;
    
        if (!(cin >> idx))                          // read index
            return err("line number expected");
    
        index.seekg(idx * sizeof(long));            // go to index-offset
    
        long
            offset;
    
        if 
        (
            !index.read                             // read the line-offset
            (
                reinterpret_cast<char *>(&offset), 
                sizeof(long)
            )            
        )
            return err("no offset for line", idx);  
    
        if (!strings.seekg(offset))                 // go to the line's offset
            return err("can't get string offet ", offset);
            
        string
            line;
        
        if (!getline(strings, line))                // read the line
            return err("no line at ", offset);
    
        cout << "Got line: " << line << endl;       // show the line
    }
    
    
    void write(ostream &index, ostream &strings)
    {
        string
            line;
    
        if (!getline(cin, line))                  // read the line
            return err("line missing");
    
        strings.seekp(0, ios::end);               // to strings 
        index.seekp(0, ios::end);                 // to index 
    
        long
            offset = strings.tellp();   
    
        if 
        (
            !index.write                          // write the offset to index
            (
                reinterpret_cast<char *>(&offset), 
                sizeof(long)
            )
        )
            err("Writing failed to index: ", offset);
    
        if (!(strings << line << endl))           // write the line itself
            err("Writing to `strings' failed");
                                                  // confirm writing the line
        cout << "Write at offset " << offset << " line: " << line << endl;
    }
        
    int main()
    {
        ifstream
            index_in("index", ios::trunc | ios::in | ios::out),
            strings_in("strings", ios::trunc | ios::in | ios::out); 
        ostream
            index_out(index_in.rdbuf()),
            strings_out(strings_in.rdbuf());

        cout << "enter `r <number>' to read line <number> or "
                                    "w <line>' to write a line\n"
                "or enter `q' to quit.\n";
    
        while (true)
        {
            cout << "r <nr>, w <line>, q ? ";       // show prompt
            string
                cmd;
    
            cin >> cmd;                             // read cmd
    
            if (cmd == "q")                         // process the cmd.
                return 0;
    
            if (cmd == "r")
                read(index_in, strings_in);
            else if (cmd == "w")
                write(index_out, strings_out);
            else
                cout << "Unknown command: " << cmd << endl;
        }
    }
Please note: