Chapter 6: The IO-stream Library

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

All C++ I/O facilities are defined in the namespace std. The std:: prefix is omitted below, except for situations where this would result in ambiguities.

Earlier (in chapter 3) we've seen several examples of the use of the C++ I/O library, in particular showing insertion operator (<<) and the extraction operator (>>). In this chapter we'll cover I/O 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 class construction has not yet been covered (for that see chapter 7) and although inheritance is not covered formally before chapter 13, it is quite possible to discuss I/O facilities long before the technical background of class construction has been covered.

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 through using declarations like:

        using ios = basic_ios<char>;

Since C++ supports various kinds of character types (e.g., char, wchar_t), I/O facilities were developed using the template mechanism allowing for easy conversions to character types other than the traditional char type. As elaborated in chapter 21, this also allows the construction of generic software, that could thereupon be used for any particular type representing characters. So, analogously to the above using declaration there exists a

        using wios = basic_ios<wchar_t>;

This way, wios can be used for the wchar_t type. Because of the existence of these type definitions, the basic_ prefix was omitted from the C++ Annotations without loss of continuity. The C++ Annotations primarily focus on the standard 8-bits char type.

Iostream objects cannot be declared using standard forward declarations, like:

    class std::ostream;     // now erroneous

Instead, to declare iostream classes the <iosfwd> header file should be included:

    #include <iosfwd>       // correct way to declare iostream classes

Using C++ I/O 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, 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. But C++ adds to this I/O based on classes, resulting in type safety, extensibility, and a clean design.

In the ANSI/ISO standard the intent was to create architecture independent I/O. Previous implementations of the iostreams library did not always comply with the standard, resulting in many extensions to the standard. The I/O sections of previously developed software may have to be partially rewritten. This is tough for those who are now forced to modify old software, but every feature and extension that was once available can be rebuilt easily using ANSI/ISO standard conforming I/O. Not all of these reimplementations can be covered in this chapter, as many reimplementations rely on inheritance and polymorphism, which topics are formally covered by chapters 13 and 14. Selected reimplementations are provided in chapter 25, and in this chapter references to particular sections in other chapters are given where appropriate.

Figure 4: Central I/O Classes

This chapter is organized as follows (see also Figure 4):

Stream objects have a limited but important role: they are 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 accessed by a streambuf object.

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- and ostream-class facilities. It is important to understand the distinction between the formatting roles of iostream objects and the buffering interface to an external device as implemented in a streambuf object. Interfacing to new devices (like sockets or file descriptors) requires the construction of a new kind of streambuf, rather than 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.

6.1: Special header files

Several iostream related header files are available. Depending on the situation at hand, the following header files should be used:

6.2: The foundation: the class `ios_base'

The class std::ios_base forms the foundation of all I/O operations, and defines, among other things, facilities for inspecting the state of I/O streams and most output formatting facilities. Every stream class of the I/O library is, through the class ios, derived from this class, and inherits its capabilities. As ios_base is the foundation on which all C++ I/O was built, we introduce it here as the first class of the C++ I/O library.

Note 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 does so, and the I/O library therefore represents a de facto I/O standard for C++. Also note that, as mentioned before, the iostream classes themselves are not responsible for the eventual I/O, but delegate this to an auxiliary class: the class streambuf or its derivatives.

It is neither possible nor required to construct an ios_base object directly. Its construction is always a side-effect of constructing an object further down the class hierarchy, like std::ios. Ios is the next class down the iostream hierarchy (see Figure 4). Since all stream classes in turn inherit from ios, and thus also from ios_base, the distinction between ios_base and ios is in practice not important. Therefore, facilities actually provided by ios_base will be 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).

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

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

Although it is possible to construct an ios object directly, this is seldom done. The purpose of the class ios is to provide the facilities of the class basic_ios, and to add several new facilites, all related to 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 of the classes ios and ios_base are also available to other stream classes. Before discussing these additional stream classes, the features offered by the class ios (and by implication: by ios_base) are now introduced.

In some cases it may be required to include ios explicitly. An example is the situations where the formatting flags themselves (cf. section 6.3.2.2) are referred to in source code.

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

6.3.1: Condition states

Operations on streams may fail for various reasons. Whenever an operation fails, further operations on the stream are suspended. It is possible to inspect, set and possibly clear the condition state of streams, allowing a program to repair the problem rather than having to abort. The members that are available for interrogating or manipulating the stream's state are described in the current section.

Conditions are represented by the following condition flags:

Several condition member functions are available to manipulate or determine the states of ios objects. Originally they returned int values, but their current return type is bool:

The following members are available to manage error states:

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

6.3.2: Formatting output and input

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

Formatting is used when it is necessary to, e.g., set the width of an output field or input buffer and to determine the form (e.g., the radix) in which values are displayed. Most formatting features belong to the realm of the ios class. Formatting is controlled by flags, defined by the ios class. These flags may be manipulated in two ways: using specialized member functions or using manipulators, which are directly inserted into or extracted from streams. There is no special reason for using either method; usually both methods are possible. In the following overview the various member functions are first introduced. Following this the flags and manipulators themselves are covered. Examples are provided showing how the flags can be manipulated and what their effects are.

Many manipulators are parameterless and are available once a stream header file (e.g., iostream) has been included. Some manipulators require arguments. To use the latter manipulators the header file iomanip must be included.

6.3.2.1: Format modifying member functions

Several member functions are available manipulating the I/O formatting flags. Instead of using the members listed below manipulators are often available that may directly be inserted into or extracted from streams. The available members are listed in alphabetical order, but the most important ones in practice are setf, unsetf and width.

6.3.2.2: Formatting flags

Most formatting flags are related to outputting information. Information can be written to output streams in basically two ways: using binary output information is written directly to an output stream, without converting it first to some human-readable format and using formatted output by which values stored in the computer's memory are converted to human-readable text first. Formatting flags are used to define the way this conversion takes place. In this section all formatting flags are covered. Formatting flags may be (un)set using member functions, but often manipulators having the same effect may also be used. For each of the flags it is shown how they can be controlled by a member function or -if available- a manipulator.

To display information in wide fields:

Using various number representations:

Fine-tuning displaying values:

Displaying floating point numbers

Handling whitespace and flushing streams

6.4: Output

In C++ output is primarily based on the std::ostream class. The ostream class defines the basic operators and members inserting information into streams: the insertion operator (<<), and special members like write writing unformatted information to streams.

The class ostream acts as base class for several other classes, all offering the functionality of the ostream class, but adding their own specialties. In the upcoming sections the following classes are discussed:

6.4.1: Basic output: the class `ostream'

The class ostream defines basic output facilities. The cout, clog and cerr objects are all ostream objects. All facilities related to output as defined by the ios class are also available in the ostream class.

We may define ostream objects using the following ostream constructor:

To define the ostream class in C++ sources, the <ostream> header file must be included. To use the predefined ostream objects (std::cerr, std::cout etc.) the <iostream> header file must be included.

6.4.1.1: Writing to `ostream' objects

The class ostream supports both formatted and binary output.

The insertion operator (<<) is 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.

The insertion operator points to the ostream object to receive the information. The normal associativity of << 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.. Each operator returns the ostream object into which the information so far has been inserted, and can thus immediately be followed by the next insertion.

Streams lack facilities for formatted output like C's printf and vprintf functions. Although it is not difficult to implement these facilities in the world of streams, printf-like functionality is hardly ever required in C++ programs. Furthermore, as it is potentially type-unsafe, it might be better to avoid this functionality completely.

When binary files must be written, normally no text-formatting is used or required: an int value should be written as a series of raw bytes, not as a series of ASCII numeric characters 0 to 9. The following member functions of ostream objects may be used to write `binary files':

6.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 at random.

The current position can be obtained and modified using the following members:

6.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 during the write-operations, and when full it is flushed.

The stream's internal buffer can be flushed under program control:

6.4.2: Output to files: the class `ofstream'

The std::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 <fstream> header file must be included. Including fstream does not automatically make available the standard streams cin, cout and cerr. Include iostream to declare these standard streams.

The following constructors are available for ofstream objects:

It is not possible to open an 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 std::streambuf object (and in some implementations: with a std::filebuf object, which is also a streambuf). Streambuf objects are discussed in section 14.8, filebuf objects are discussed in section 14.8.2.

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

6.4.2.1: Modes for opening stream objects

The following file modes or file flags are available when constructing or opening ofstream (or istream, see section 6.5.2) objects. The values are of type ios::openmode. Flags may be combined using the bitor operator. The following combinations of file flags have special meanings:
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.

An interesting subtlety is that the open members of the ifstream, ofstream and fstream classes have a second parameter of type ios::openmode. In contrast to this, the bitor operator returns an int when applied to two enum-values. The question why the bitor operator may nevertheless be used here is answered in a later chapter (cf. section 11.13).

6.4.3: Output to memory: the class `ostringstream'

To write information to memory using stream facilities, std::ostringstream objects should be used. As the class ostringstream is derived from the class ostream all ostream's facilities are available to ostringstream objects as well. To use and define ostringstream objects the header file <sstream> must be included. In addition the class ostringstream offers the following constructors and members: The following example illustrates the use of the ostringstream class: several values are inserted into the object. Then, the text contained by the ostringstream object is stored in a std::string, whose length and content are thereupon printed. Such ostringstream objects are most often used for doing `type to string' conversions, like converting int values to text. Formatting flags can be used with ostringstreams as well, as they are part of the ostream class.

Here is an example showing an ostringstream object being used:

    #include <iostream>
    #include <sstream>

    using namespace std;

    int main()
    {
        ostringstream ostr("hello ", ios::ate);

        cout << ostr.str() << '\n';

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

        cout << ostr.str() << '\n';

        ostr << " -- ";
        ostr.unsetf(ios::hex);
        ostr << 12;

        cout << ostr.str() << '\n';

        ostr.str("new text");
        cout << ostr.str() << '\n';

        ostr.seekp(4, ios::beg);
        ostr << "world";
        cout << ostr.str() << '\n';
    }
    /*
        Output from this program:
    hello
    hello 0x3039
    hello 0x3039 -- 12
    new text
    new world
    */

6.4.4: The `put_time' manipulator

The manipulator std::put_time(std::tm const *specs, char const *fmt) can be used to insert time specifications into std::ostream objects.

Time specifications are provided in std::tm objects, and the way the time should be displayed is defined by the format string fmt.

Starting with a chrono::time_point the following steps must be performed to insert the time point's time into a std::ostream:

A simple function returning put_time's return value and expecting a time_point and format string can be defined which handles the above two statements. E.g., (omitting the std:: and std::chrono:: specifications for brevity):

    auto localTime(time_point<system_clock> const &tp, char const *fmt)
    {
        time_t secs = system_clock::to_time_t( tp );
        return put_time(localtime(&secs), fmt);
    }
                // used as:
    cout << localTime(system_clock{}.now(), "%c") << '\n';

Many more format specifiers are recognized by put_time. Specifiers start with %. To display a percent character as part of the format string write it twice: %%. In addition to the standard escape sequences, %n can be used instead of \n, and %t can be used instead of \t.


5
Specifier Meaning
std::tm field(s)

%Y
 
year as a 4 digit decimal number
 
tm_year
%EY
 
year in an alternative representation
 
tm_year
%y
 
last 2 digits of year as a decimal number (range [00,99])
 
tm_year
%Oy
 
last 2 digits of year using an alternative numeric system
 
tm_year
%Ey
 
year as offset from locale's alternative calendar period %EC (locale-dependent)
 
tm_year
%C
 
first 2 digits of year as a decimal number (range [00,99])
 
tm_year
%EC
 
name of the base year (period) in the locale's alternative representation
 
tm_year
%G
 
ISO 8601 week-based year, i.e. the year that contains the specified week
 
tm_year,
tm_wday,
tm_yday
%g
 
last 2 digits of ISO 8601 week-based year (range [00,99])
 
tm_year,
tm_wday,
tm_yday


5
Specifier Meaning
std::tm field(s)

%b
 
abbreviated month name, e.g. Oct
 
tm_mon
%m
 
month as a decimal number (range [01,12])
 
tm_mon
%Om
 
month using an alternative numeric system
 
tm_mon


5
Specifier Meaning
std::tm field(s)

%U
 
week of the year as a decimal number (Sunday is the first day of the week) (range [00,53])
 
tm_year,
tm_wday,
tm_yday
%OU
 
week of the year, as by %U, using an alternative numeric system
 
tm_year,
tm_wday,
tm_yday
%W
 
week of the year as a decimal number (Monday is the first day of the week) (range [00,53])
 
tm_year,
tm_wday,
tm_yday
%OW
 
week of the year, as by %W, using an alternative numeric system
 
tm_year,
tm_wday,
tm_yday
%V
 
ISO 8601 week of the year (range [01,53])
 
tm_year,
tm_wday,
tm_yday
%OV
 
week of the year, as by %V, using an alternative numeric system
 
tm_year,
tm_wday,
tm_yday


5
Specifier Meaning
std::tm field(s)

%j
 
day of the year as a decimal number (range [001,366])
 
tm_yday
%d
 
day of the month as a decimal number (range [01,31])
 
tm_mday
%Od
 
zero-based day of the month using an alternative numeric system
 
tm_mday
%e
 
day of the month as a decimal number (range [1,31])
 
tm_mday
%Oe
 
one-based day of the month using an alternative numeric system
 
tm_mday


5
Specifier Meaning
std::tm field(s)

%a
 
abbreviated weekday name, e.g. Fri
 
tm_wday
%A
 
full weekday name, e.g. Friday
 
tm_wday
%w
 
weekday as a decimal number, where Sunday is 0 (range [0-6])
 
tm_wday
%Ow
 
weekday, where Sunday is 0, using an alternative numeric system
 
tm_wday
%u
 
weekday as a decimal number, where Monday is 1 (ISO 8601 format) (range [1-7])
 
tm_wday
%Ou
 
weekday, where Monday is 1, using an alternative numeric system
 
tm_wday


5
Specifier Meaning
std::tm field(s)

%H
 
hour as a decimal number, 24 hour clock (range [00-23])
 
tm_hour
%OH
 
hour from 24-hour clock using an alternative numeric system
 
tm_hour
%I
 
hour as a decimal number, 12 hour clock (range [01,12])
 
tm_hour
%OI
 
hour from 12-hour clock using the alternative numeric system
 
tm_hour
%M
 
minute as a decimal number (range [00,59])
 
tm_min
%OM
 
minute using an alternative numeric system
 
tm_min
%S
 
second as a decimal number (range [00,60])
 
tm_sec
%OS
 
second using an alternative numeric system
 
tm_sec


5
Specifier Meaning
std::tm field(s)

%c
 
standard date and time string, e.g. Sun Oct 17 04:41:13 2010
 
all
%Ec
 
alternative date and time string
 
all
%x
 
localized date representation
 
all
%Ex
 
alternative date representation
 
all
%X
 
localized time representation
 
all
%EX
 
alternative time representation
 
all
%D
 
equivalent to "%m/%d/%y"
 
tm_mon,
tm_mday,
tm_year
%F
 
equivalent to "%Y-%m-%d" (the ISO 8601 date format)
 
tm_mon,
tm_mday,
tm_year
%r
 
localized 12-hour clock time
 
tm_hour,
tm_min,
tm_sec
%R
 
equivalent to "%H:%M"
 
tm_hour,
tm_min
%T
 
equivalent to "%H:%M:%S" (the ISO 8601 time format)
 
tm_hour,
tm_min,
tm_sec
%p
 
localized a.m. or p.m.
 
tm_hour
%z
 
offset from UTC in the ISO 8601 format (e.g. -0430;
no characters if time zone information is not available)
 
tm_isdst
%Z
 
time zone name or abbreviation
(no characters if time zone information is not available)
 
tm_isdst

6.5: Input

In C++ input is primarily based on the std::istream class. The istream class defines the basic operators and members extracting information from streams: the extraction operator (>>), and special members like istream::read reading unformatted information from streams.

The class istream acts as base class for several other classes, all offering the functionality of the istream class, but adding their own specialties. In the upcoming sections the following classes are discussed:

6.5.1: Basic input: the class `istream'

The class istream defines basic input facilities. The cin object, is an istream object. All facilities related to input as defined by the ios class are also available in the istream class.

We may define istream objects using the following istream constructor:

To define the istream class in C++ sources, the <istream> header file must be included. To use the predefined istream object cin, the <iostream> header file must be included.

6.5.1.1: Reading from `istream' objects

The class istream supports both formatted and unformatted (binary) input. The extraction operator (operator>>) is 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.

The extraction operator points to the objects or variables to receive new values. The normal associativity of >> 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 many (overloaded) variants and thus many types of variables can be extracted from istream objects. There is an overloaded >> available for the extraction of an int, of a double, of a string, of an array of characters, possibly to the location pointed at by a pointer, etc., etc.. String or character array extraction by default first skips all whitespace characters, and then extracts all consecutive non-whitespace characters. Once an extraction operator has been processed the istream object from which the information was extracted is returned and it can immediately be used for additional istream operations that appear in the same expression.

Streams do not support facilities for formatted input as offered by C's scanf and vscanf functions. Although it is not difficult to add such facilities to the world of streams, scanf-like functionality is in practice never needed in C++ programs. Furthermore, as it is potentially type-unsafe, it is better to avoid using C-type formatted input.

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:

6.5.1.2: `istream' positioning

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

The current position can be obtained and modified using the following members:

6.5.2: Input from files: the class `ifstream'

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

In order to use the ifstream class in C++ sources, the <fstream> header file must be included. Including fstream does not automatically make available the standard streams cin, cout and cerr. Include iostream to declare these standard streams.

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.

6.5.3: Input from memory: the class `istringstream'

To read information from memory using stream facilities, std::istringstream objects should be used. As the class istringstream is derived from the class istream all istream's facilities are available to istringstream objects as well. To use and define istringstream objects the header file <sstream> must be included. In addition the class istringstream offers the following constructors and members: The following example illustrates the use of the istringstream class: several values are extracted from the object. Such istringstream objects are most often used for doing `string to type' conversions, like converting text to int values (cf. C's atoi function). Formatting flags can be used with istringstreams as well, as they are part of the istream class. In the example note especially the use of the member seekg:
    #include <iostream>
    #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 << '\n';          // write it out
        istr.seekg(0);              // retry from the beginning
        istr >> x;                  // extract int
        cout << x << '\n';          // write it out
        istr.str("666");            // store another text
        istr >> x;                  // extract it
        cout << x << '\n';          // write it out
    }
    /*
        output of this program:
    3
    123
    666
    */

6.5.4: Copying streams

Usually, files are copied either by reading a source file character by character or line by line. The basic mold to process streams is as follows: Note that reading must precede testing, as it is only possible to know after actually attempting to read from a file whether the reading succeeded or not. Of course, variations are possible: getline(istream &, string &) (see section 6.5.1.1) returns an istream &, so here reading and testing may be contracted using one expression. Nevertheless, the above mold represents the general case. So, the following program may 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;
    }
}

Contraction is possible here by combining get with the if-statement, resulting in:

    if (!cin.get(c))
        break;

Even so, this would still follow the basic rule: `read first, test later'.

Simply copying a file isn't required very often. More often a situation is encountered where a file is processed up to a certain point, followed by plain copying the file's remaining information. The next program illustrates this. Using ignore 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 uses yet another overloaded version of the <<-operator, in which a streambuf pointer is inserted into a stream. As the member rdbuf returns a stream's streambuf *, we have a simple means of inserting a stream's content into an ostream:

    #include <iostream>
    using namespace std;

    int main()
    {
        cin.ignore(80, '\n');   // skip the first line and...
        cout << cin.rdbuf();    // copy the rest through the streambuf *
    }
This way of copying streams only assumes the existence of a streambuf object. Consequently it can be used with all specializations of the streambuf class.

6.5.5: Coupling streams

Ostream objects can be coupled to ios objects using the tie member function. Tying results in flushing the ostream's buffer whenever an input or output operation is performed on the ios object to which the ostream object is tied. By default cout is tied to cin (using cin.tie(cout)). This tie means that whenever an operation on cin is requested, cout is flushed first. To break the tie, ios::tie(0) can be called. In the example: cin.tie(0).

Another useful coupling of streams is shown by the tie between cerr and cout. Because of the tie standard output and error messages written to the screen are shown in sync with the time at which they were generated:

    #include <iostream>
    using namespace std;

    int main()
    {
        cerr.tie(0);        // untie
        cout << "first (buffered) line to cout ";
        cerr << "first (unbuffered) line to cerr\n";
        cout << "\n";

        cerr.tie(&cout);    // tie cout to cerr
        cout << "second (buffered) line to cout ";
        cerr << "second (unbuffered) line to cerr\n";
        cout << "\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 implemented 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 stream library rather than operating system calls can be implemented. See the next sections for examples.

6.6: Advanced topics

6.6.1: Moving streams

Stream classes (e.g.,, all stream classes covered in this chapter) are movable and can be swapped. This implies that factory functions can be designed for stream classes. Here is an example:
    ofstream out(string const &name)
    {
        ofstream ret(name);             // construct ofstream
        return ret;                     // return value optimization, but
    }                                   // OK as moving is supported
    
    int main()
    {
        ofstream mine(out("out"));      // return value optimizations, but
                                        // OK as moving is supported

        ofstream base("base");
        ofstream other;

        base.swap(other);               // swapping streams is OK

        other = std::move(base);        // moving streams is OK

        // other = base;                // this would fail: copy assignment
                                        // is not available for streams
    }

6.6.2: Redirecting streams

Using ios::rdbuf streams can be forced to share their streambuf objects. Thus information written to one stream is actually written to another stream; a phenomenon normally called redirection. Redirection is commonly implemented at the operating system level, and sometimes that is still necessary (see section 25.2.3).

A common situation where redirection is useful is when error messages should be written to file rather than to the standard error stream, 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

Following this command any error messages written by program are written to /tmp/error.log, instead of appearing on the screen.

Here is an example showing how this can be implemented using streambuf objects. Assume program expects an argument defining the name of the file to write the error messages to. It could be called as follows:

    program /tmp/error.log

The program looks like this, an explanation is provided below the program's source text:

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

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

        if (argc == 2)
        {
            errlog.open(argv[1]);                       // 3
            cerr_buffer = cerr.rdbuf(errlog.rdbuf());   // 4
        }
        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();                                      // 5

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

        cerr.rdbuf(cerr_buffer);                        // 6
        cerr << "Done\n";                               // 7
    }
    /*
        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
    */

6.6.3: Reading AND Writing streams

Streams can be read and written using std::fstream objects. As with ifstream and ofstream objects, its constructor expects the name of the file to be opened:
        fstream inout("iofile", ios::in | ios::out);

Note the use of the 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 bitor operator. Alternatively, instead of ios::out, ios::app could have been used and mere writing would become appending (at the end of the file).

Reading and writing to the same file is always a bit awkward: what to do when the file may not yet exist, but if it already exists it should not be rewritten? Having fought with this problem for some time I now use the following approach:

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

    using namespace std;

    int main()
    {
        fstream rw("fname", ios::out | ios::in);

        if (!rw)            // file didn't exist yet
        {
            rw.clear();     // try again, creating it using ios::trunc
            rw.open("fname", ios::out | ios::trunc | ios::in);
        }

        if (!rw)            // can't even create it: bail out
        {
            cerr << "Opening `fname' failed miserably" << '\n';
            return 1;
        }

        cerr << "We're at: " << rw.tellp() << '\n';

                            // write something
        rw << "Hello world" << '\n';

        rw.seekg(0);        // go back and read what's written

        string s;
        getline(rw, s);

        cout << "Read: " << s << '\n';
    }
Under this approach if the first construction attempt fails fname doesn't exist yet. But then open can be attempted using the ios::trunc flag. If the file already existed, the construction would have succeeded. By specifying ios::ate when defining rw, the initial read/write action would by default have taken place at EOF.

Under DOS-like operating systems that use the multiple character sequence \r\n to separate lines in text files the flag ios::binary is required to process binary files ensuring that \r\n combinations are processed as two characters. In general, ios::binary should be specified when binary (non-text) files are to be processed. By default files are opened as text files. Unix operating systems do not distinguish text files from binary files.

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 6.4.2.1 for details.

Once a file has been opened in read and write mode, the << operator can be used to insert information into the file, while the >> operator may be used to extract information from the file. These operations may be performed in any order, but a seekg or seekp operation is required when switching between insertions and extractions. The seek operation is used to activate the stream's data used for reading or those used for writing (and vice versa). The istream and ostream parts of fstream objects share the stream's data buffer and by performing the seek operation the stream either activates its istream or its ostream part. If the seek is omitted, reading after writing and writing after reading simply fails. The example shows a whitespace-delimited word being read from a file, writing another string to the file, just beyond the point where the just read word terminated. Finally yet another string is read which is found just beyond the location where the just written strings ended:

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

    f >> str;       // read the first word

                    // write a well-known text
    f.seekp(0, ios::cur);
    f << "hello world";

    f.seekg(0, ios::cur);
    f >> str;       // and read again

Since a seek or clear operation is required when alternating between read and write (extraction and insertion) operations on the same file it is not possible to execute a series of << and >> operations in one expression statement.

Of course, random insertions and extractions are hardly ever used. Generally, insertions and extractions occur at well-known locations in a file. In those cases, the position where insertions or extractions are required can be controlled and monitored by the seekg, seekp, tellg and tellp members (see sections 6.4.1.2 and 6.5.1.2).

Error conditions (see section 6.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 by the clear member function. Following clear processing may continue. E.g.,

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

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

    f >> str;       // read the first word

A situation where files are both read and written is seen in database applications, using files consisting of records having fixed sizes, and where locations and sizes of pieces of information are known. For example, the following program adds text lines to a (possibly existing) file. It can also be used to retrieve a particular line, given its order-number in the file. A binary file index allows for the quick retrieval of the location of lines.

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

    void err(char const *msg)
    {
        cout << msg << '\n';
    }

    void err(char const *msg, long value)
    {
        cout << msg << value << '\n';
    }

    void read(fstream &index, fstream &strings)
    {
        int idx;

        if (!(cin >> idx))                          // read index
        {
            cin.clear();                            // allow reading again
            cin.ignore(INT_MAX, '\n');              // skip the line
            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 offset ", offset);

        string line;

        if (!getline(strings, line))                // read the line
            return err("no line at ", offset);

        cout << "Got line: " << line << '\n';       // 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)
            )
        )
            return err("Writing failed to index: ", offset);

        if (!(strings << line << '\n'))           // write the line itself
            return err("Writing to `strings' failed");
                                                  // confirm writing the line
        cout << "Write at offset " << offset << " line: " << line << '\n';
    }

    int main()
    {
        fstream index("index", ios::trunc | ios::in | ios::out);
        fstream 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

            index.clear();
            strings.clear();

            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 if (cin.eof())
            {
                cout << "\n"
                        "Unexpected end-of-file\n";
                return 1;
            }
            else
                cout << "Unknown command: " << cmd << '\n';
        }
    }
Another example showing reading and writing of files is provided by the next program. It also illustrates the processing of NTBSs:
    #include <iostream>
    #include <fstream>
    using namespace std;

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

        f.write("hello", 6);                // write 2 NTB strings
        f.write("hello", 6);

        f.seekg(0, ios::beg);               // reset to begin of file

        char buffer[100];                   // or: char *buffer = new char[100]
        char c;
                                            // read the first `hello'
        cout << f.get(buffer, sizeof(buffer), 0).tellg() << '\n';
        f >> c;                             // read the NTB delim

                                            // and read the second `hello'
        cout << f.get(buffer + 6, sizeof(buffer) - 6, 0).tellg() << '\n';

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

A completely different way to read and write streams may be implemented using streambuf members. All considerations mentioned so far remain valid (e.g., before a read operation following a write operation seekg must be used). When streambuf objects are used, either an istream is associated with the streambuf object of another ostream object, or an ostream object is associated with the streambuf object of another istream object. Here is the previous program again, now using associated streams:

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

    void err(char const *msg);      // see earlier example
    void err(char const *msg, long value);

    void read(istream &index, istream &strings)
    {
        index.clear();
        strings.clear();

        // insert the body of the read() function of the earlier example
    }


    void write(ostream &index, ostream &strings)
    {
        index.clear();
        strings.clear();

        // insert the body of the write() function of the earlier example
    }

    int main()
    {
        ifstream index_in("index", ios::trunc | ios::in | ios::out);
        ifstream strings_in("strings", ios::trunc | ios::in | ios::out);
        ostream  index_out(index_in.rdbuf());
        ostream  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 << '\n';
        }
    }
In this example

Like fstream objects string-stream objects can also be used for reading and writing. After including the <sstream> header file a std::stringstream can be defined which supports both reading and writing. After inserting information into a stringstream object seekg(0) can be called to read its info from the beginning of its content. When a stringstream must repeatedly be used for reading and writing call its clear and str members before starting a new writing cycle. Alternatively, a stringstream str can be reinitialized using str = stringstream{}. Here is an example:

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

    int main(int argc, char **argv)
    {
        stringstream io;
        for (size_t redo = 0; redo != 2; ++redo)
        {
            io.clear();                 // clears the not-good flags
            io.str("");
            io << argv[0] << '\n';

            io.seekg(0);
            string line;
            while (getline(io, line))   // results in io.eof()
                cout << line << '\n';
        }
    }