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.1.0d) 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, analogous to the above typedef there exists a

typedef basic_ios<wchar_t> wios;

type definition which 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 anymore correct 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, resepctively. 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 figure 3):

  • The class ios_base represents the foundation upon with the iostreams I/O library was built. The class ios forms the foundation of all I/O operations, and defines, among other things, the facilities for inspecting the state of I/O streams and output formatting.
  • The class ios was directly derived from ios_base. Every class of the I/O library doing input or output is derived from this ios class, and inherits its (and, by implication, ios_base's) capabilities. The reader is urged to keep this feature in mind while reading this chapter. The concept of inheritance is not discussed further here, but rather in chapter 13.

    An important function of the class ios is to define the communication with the buffer that is used by streams. The buffer is a streambuf object (or is derived from the class streambuf) and is responsible for the actual input and/or output. This means that iostream objects do not perform input/output operations themselves, but leave these to the (stream)buffer objects with which they are associated.

  • Next, basic C++ output facilities are discussed. The basic class used for output is ostream, defining the insertion operator as well as other facilities for writing information to streams. Apart from inserting information in files it is possible to insert information in memory buffers, for which the ostringstream class is available. Formatting of the output is to a great extent possible using the facilities defined in the ios class, but it is also possible to insert formatting commands directly in streams, using manipulators. This aspect of C++ output is discussed as well.
  • Basic C++ input facilities are available in the istream class. This class defines the insertion operator and related facilities for input. Analogous to the ostringstream a class istringstream class is available for extracting information from memory buffers.
  • Finally, several advanced I/O-related topics are discussed: other topics, combined reading and writing using streams and mixing C and C++ I/O using filebuf ojects. Other I/O related topics are covered later in the Annotations, e.g., in chapter 19.
  • 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:
  • #include <iosfwd>: sources should use this preprocessor directive if a forward declaration is required for the iostream classes. For example, if a function defines a reference parameter to an ostream then, when this function itself is declared, there is no need for the compiler to know exactly what an ostream is. In the header file declaring such a function the ostream class merely needs to be be declared. One cannot use
        class ostream;      // erroneous declaration
    
        void someFunction(ostream &str);
    
    but, instead, one should use:
        #include <iosfwd>   // correctly declares class ostream
    
        void someFunction(ostream &str);
    
  • #include <streambuf>: sources should use this preprocessor directive when using streambuf or filebuf classes. See sections 5.7 and 5.7.2.
  • #include <istream>: sources should use this preprocessor directive when using the class istream or when using classes that do both input and output. See section 5.5.1.
  • #include <ostream>: sources should use this ipreprocessor directive when using the class ostream class or when using classes that do both input and output. See section 5.4.1.
  • #include <iostream>: sources should use this preprocessor directive when using the global stream objects (like cin and cout).
  • #include <fstream>: sources should use this preprocessor directive when using the file stream classes. See sections 5.5.2, 5.4.2 and 5.8.4.
  • #include <sstream>: sources should use this preprocessor directive when using the string stream classes. See sections 5.4.3 and 5.5.3.
  • #include <iomanip>: sources should use this preprocessor directive when using parameterized msnipulators. See section 5.6
  • 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 basis 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 derived from it.

    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:

  • streambuf *ios::rdbuf():
    This member function returns a pointer to the streambuf object forming the interface between the ios object and the device with which the ios object communicates. See section 19.1.2 for further information about the class streambuf.
  • streambuf *ios::rdbuf(streambuf *new):
    This member function can be used to associate a ios object with another streambuf object. A pointer to the ios object's original streambuf object is returned. The object to which this pointer points is not destroyed when the stream object goes out of scope, but is owned by the caller of rdbuf().
  • ostream *ios::tie():
    This member function returns a pointer to the ostream object that is currently tied to the ios object. The returned ostream object is flushed every time before information is input or output to the ios object of which the tie() member is called. The return value 0 indicates that currently no ostream object is tied to the ios object. See section 5.8.2 for details.
  • ostream *ios::tie(ostream *new):
    This member function can be used to associate an ios object with another ostream object. A pointer to the ios object's original ostream object is returned. See section 5.8.2 for details.
  • 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:

  • ios::badbit:
    if this flag has been raised an illegal operation has been performed, like an attempt to read beyond end-of-file.
  • ios::eofbit:
    if this flag has been raised, the ios object has sensed end of file.
  • ios::failbit:
    if this flag has been raised, an operation has failed, like an attempt to extract an int when no numeric characters are available on input.
  • ios::goodbit:
    this flag indicates that no failure has been sensed for the ios object. The ios::failbit and ios::goodbit are each other's complements.
  • Several condition member functions are available to manipulate or determine the states of ios objects:

  • ios::bad():
    this member function returns a non-zero value when an invalid operation has been requested (i.e., when ios::goodbit has been set).
  • ios::eof():
    this member function returns a non-zero value when end of file (EOF) has been sensed (i.e., ios::eofbit has been set).
  • ios::fail():
    this member function returns a non-zero value when ios::eof() or ios::bad() returns a non-zero value.
  • ios::good():
    this member function returns a non-zero value when ios::fail() returns a zero value and vice versa.
  • 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:

  • ios::clear():
    When an error condition has occurred, and the condition can be repaired, then clear() can be called to clear the error status of the file. An overloaded version accepts state flags, which are set after first clearing the current set of flags: ios::clear(int state)
  • ios::rdstate():
    This member function returns the current set of flags that are set for an ios object. To test for a particular flag, use the bitwise and operator:
        if (iosObject.rdstate() & ios::good)    
        {   
            // state is good
        }
    
  • ios::setstate(int flags):
    This member is used to set a particular set of flags. The member ios::clear() is a shortcut to clear all error flags. Of course, clearing the flags doesn't automatically mean the errorcondition has been cleared too. The strategy should be:
  • An error condition is detected,
  • The error is repaired
  • The member ios::clear() is called.
  • 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):

  • ios::adjustfield:
    mask value used in combination with a flag setting defining the way values are adjusted in wide fields (ios::left, ios::right, ios::internal).
  • ios::basefield:
    mask value used in combination with a flag setting the radix of integral values to output (ios::dec, ios::hex or ios::oct).
  • ios::boolalpha:
    to display boolean values as text, using the text ``true'' for the true logical value, and the string ``false'' for the false logical value. By default this flag is not set. Corresponding manipulators: ios::boolalpha, ios::noboolalpha.
  • ios::dec:
    to read and display integral values as decimal (i.e., radix 10) values. This is the default. With setf() the mask value ios::basefield must be provided. Corresponding manipulator: ios::dec.
  • ios::fixed:
    to display real values in a fixed notation (e.g., 12.25), as opposed to displaying values in a scientific notation. With setf() the mask value ios::floatfield must be provided. Corresponding manipulator: ios::fixed.
  • ios::floatfield:
    mask value used in combination with a flag setting the way real numbers are displayed (ios::fixed or ios::scientific).
  • ios::hex:
    to read and display integral values as hexadecimal values (i.e., radix 16) values. With setf() the mask value ios::basefield must be provided. Corresponding manipulator: ios::hex.
  • ios::internal:
    to add fill characters (blanks by default) between the minus sign of negative numbers and the value itself. With setf() the mask value adjustfield must be provided. Corresponding manipulator: ios::internal.
  • ios::left:
    to left-adjust (integral) values in fields that are wider than needed to display the values. By default values are right-adjusted (see below). With setf() the mask value adjustfield must be provided. Corresponding manipulator: ios::left.
  • ios::oct:
    to display integral values as octal values (i.e., radix 8) values. With setf() the mask value ios::basefield must be provided. Corresponding manipulator: ios::oct.
  • ios::right:
    to right-adjust (integral) values in fields that are wider than needed to display the values. This is the default adjustment. With setf() the mask value adjustfield must be provided. Corresponding manipulator: ios::right.
  • ios::scientific:
    to display real values in scientific notation (e.g., 1.24e+03). With setf() the mask value ios::floatfield must be provided. Corresponding manipulator: ios::scientific.
  • ios::showbase:
    to display the numeric base of integral values. With hexadecimal values the 0x prefix is used, with octal values the prefix 0. For the (default) decimal value no particular prefix is used. Corresponding manipulators: ios::showbase and ios::noshowbase
  • ios::showpoint:
    display a trailing decimal point and trailing decimal zeros when real numbers are displayed. When this flag is set, an insertion like:
    cout << 16.0 << ", " << 16.1 << ", " << 16 << endl;

    could result in:

    16.0000, 16.1000, 16

    Note that the last 16 is an integral rather than a real number, and is not given a decimal point: ios::showpoint has no effect here. If ios::showpoint is not used, then trailing zeros are discarded. If the decimal part is zero, then the decimal point is discarded as well. Corresponding manipulator: ios::showpoint.

  • ios::showpos:
    display a + character with positive values. Corresponding manipulator: ios::showpos.
  • ios::skipws:
    used for extracting information from streams. When this flag is is (which is the default) leading white space characters (blanks, tabs, newlines, etc.) are skipped when a value is extracted from a stream. If the flag is not set, leading white space characters are not skipped.
  • ios::unitbuf:
    flush the stream after each output operation.
  • ios::uppercase:
    use capital letters in the representation of (hexadecimal or scientifically formatted) values.
  • 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:

  • ios &copyfmt(ios &obj):
    This member function copies all format definitions from obj to the current ios object.
  • ios::fill() const:
    returns (as char) the current padding character. By default, this is the blank space.
  • ios::fill(char padding):
    redefines the padding character. Returns (as char) the previous padding character. Corresponding manipulator: ios::setfill().
  • ios::flags() const:
    returns the current collection of flags controlling the format state of the stream for which the member function is called. To inspect a particular flag, use the binairy and operator, e.g.,
            if (cout.flags() & ios::hex)
            {
                // hexadecimal output of integral values
            }
    
  • ios::flags(fmtflags flagset):
    returns the previous set of flags, and defines the current set of flags as flagset, defined by a combination of formatting flags, combined by the binary or operator.
  • ios::precision() const:
    returns (as int) the number of significant digits used for outputting real values (default: 6).
  • ios::precision(int signif):
    redefines the number of significant digits used for outputting real values, returns (as int) the previously used number of significant digits. Corresponding manipulator: ios::setprecision().
  • ios::setf(fmtflags flags):
    returns the previous set of all flags, and sets one or more formatting flags (using the bitwise operator|() to combine multiple flags. Other flags are not affected). Corresponding manipulators: ios::setiosflags and ios::resetiosflags
  • ios::setf(fmtflags flags, fmtflags mask):
    returns the previous set of all flags, clears all flags mentioned in mask, and sets the flags specified in flags. Well-known mask values are ios::adjustfield, ios::basefield and ios::floatfield. For example:
  • setf(ios::left, ios::adjustfield) is used to left-adjust wide values in their field. (alternatively, ios::right and ios::internal can be used).
  • setf(ios::hex, ios::basefield) is used to activate the hexadecimal representation of integral values (alternatively, ios::dec and ios::oct can be used).
  • setf(ios::fixed, ios::floatfield) is used to activate the fixed value representation of real values (alternatively, ios::scientific can be used).
  • ios::unsetf(fmtflags flags):
    returns the previous set of all flags, and clears the specified formatting flags (leaving the remaining flags unaltered). The unsetting of an active default flag (e.g., cout.unsetf(ios::dec)) has no effect.
  • ios::width() const:
    returns (as int) the current output field width (the number of characters to write for numerical values on the next insertion operation). Default: 0, meaning `as many characters as needed to write the value'. Corresponding manipulator: ios::setw().
  • ios::width(int nchars):
    returns (as int) the previously used output field width, redefines the value to nchars for the next insertion operation. Note that the field width is reset to 0 after every insertion operation, and that width() currently has no effect on text-values like char * or string values.
  • 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:

  • The class ostream, offering the basic facilities for doing output;
  • The class ofstream, allowing us to open files for writing (comparable to C's fopen(filename, "w"));
  • The class ostringstream, allowing us to write informatioon to memory rather than to files (streams) (comparable to C's sprintf() function).
  • 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:

  • ostream object(streambuf *sb):
    this constructor can be used to construct a wrapper around an existing open stream, based on an existing streambuf, which may be the interface to an existing file. See chapter 19 for examples.
  • 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:

  • ostream& ostream::put(char c):
    This member function
    writes a single character to the output stream. Since a character is a byte, this member function could also be used for writing a single character to a text-file.
  • ostream& ostream::write(char const *buffer, int length):
    This member function writes at most len bytes, stored in the void const *buffer to the ostream object. The bytes are written as they are stored in the buffer, no formatting is done whatsoever.
  • 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:

  • pos_type ostream::tellp():
    this function returns the current (absolute) position where the next write-operation to the stream will take place. For all practical purposes a pos_type can be considered to be an unsigned long.
  • ostream &ostream::seekp(off_type step, ios::seekdir org):
    This member function can be used to reposition the stream. The function expects an off_type step, the stepsize in bytes to go from org. For all practical purposes a off_type can be considered to be a long. The origin of the step, org is an ios::seekdir value. Possible values are:
  • ios::beg:
    org is interpreted as the stepsize relative to the beginning of the stream. If org is not specified, ios::beg is used.
  • ios::cur:
    org is interpreted as the stepsize relative to the current position (as returned by tellp() of the stream).
  • ios::end:
    org is interpreted as the stepsize relative to the current end position of the the stream.
  • It is allowed to seek beyond end of file . Writing bytes to a location beyond EOF will pad the intermediate bytes with ASCII-Z values: null-bytes. It is not allowed to seek before begin of file. Seeking before ios::beg will cause the ios::fail flag to be set.
  • 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:

  • ostream& ostream::flush():
    this member function writes any buffered information to the ostream object. The call to flush() is implied when:
  • The ostream object ceases to exist,
  • The endl or flush manipulators (see section 5.6) are inserted into the ostream object,
  • A stream derived from ostream (like ofstream, see section 5.4.2) is closed.
  • 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:

  • ofstream object:
    This is the basic constructor. It creates an ofstream object which may be associated with an actual file later, using the open() member (see below).
  • ofstream object(char const *name, int mode):
    This constructor can be used to associate an ofstream object with the file named name, using output mode mode. The output mode is by default ios::out. See section 5.4.2.1 for a complete overview of available output modes.

    In the following example an ofstream object, associated with the newly created file /tmp/scratch, is constructed:

        ofstream out("/tmp/scratch");
    
  • 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.

  • void ofstream::open(char const *name, int mode, int perm):
    Having constructed an ofstream object, the member function open() can be used to associate the ofstream object with an actual file.
  • ofstream::close():
    Conversely, it is possible to close an ofstream object explicitly using the close() member function. The function sets the ios::fail flag of the closed object. Closing the file will flush any buffered information to the associated file. A file is automatically closed when the associated ofstream object ceases to exist.
  • 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:

  • ios::app:
    reposition to the end of the file before every output command. The existing contents of the file are kept.
  • ios::ate:
    Start initially at the end of the file. The existing contents of the file are kept.
  • ios::binary:
    open a binary file (used on systems which make a distinction between text- and binary files, like MS-DOS or MS-Windows).
  • ios::in:
    open the file for reading. The file must exist.
  • ios::out:
    open the file. Create it if it doesn't yet exist. If it exists, the file is rewritten.
  • ios::trunc:
    Start initially with an empty file. Any existing contents of the file are lost.
  • 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:
  • ostringstream ostr(string const &s, ios::openmode mode):
    When using this constructor, the last or both arguments may be omitted. These is also a constructor available having only an openmode parameter. If string s is specified and openmode is ios::ate, the ostringstream object is initialized with the string s and remaining insertions are appended to the contents of the ostringstream object. If string s is provided, it will not be altered, as any information inserted into the object is stored in dynamically allocated memory which is deleted when the ostringstream object goes out of scope.
  • string ostringstream::str() const:
    This member function will return the string that is stored inside the ostringstream object.
  • 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:

  • The class istream, offering the basic facilities for doing input;
  • The class ifstream, allowing us to open files for reading (comparable to C's fopen(filename, "r"));
  • The class istringstream, allowing us to read informatioon from text that is not stored on files (streams) but in memory (comparable to C's sscanf() function).
  • 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:

  • istream object(streambuf *sb):
    this constructor can be used to construct a wrapper around an existing open stream, based on an existing streambuf, which may be the interface to an existing file. See chapter 19 for examples.
  • 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:

  • int istream::gcount():
    this function does not actually read from the input stream, but returns the number of characters that were read from the input stream during the last unformated input operation.
  • int istream::get():
    this function returns EOF or reads and returns the next available single character as an int value.
  • istream &istream::get(char &c):
    this function reads the next single character from the input stream into c. As its return value is the stream itself, its return value can be queried to determine whether the extraction succeeded or not.
  • istream& istream::get(char *buffer, int len [, char delim]):
    This function reads a series of len - 1 characters from the input stream into the array starting at buffer, which should be at least len bytes long. At most len - 1 characters are read into the buffer. By default, the delimiter is a newline ('\n') character. The delimiter itself is not removed from the input stream.

    After reading the series of characters into buffer, an ASCII-Z character is written beyond the last character that was written to buffer. The functions eof() and fail() (see section 5.3.1) return 0 (false) if the delimiter was not encountered before len - 1 characters were read. Furthermore, an ASCII-Z can be used for the delimiter: this way strings terminating in ASCII-Z characters may be read from a (binary) file. The program using this get() member function should know in advance the maximum number of characters that are going to be read.

  • istream& istream::getline(char *buffer, int len [, char delim]):
    This function operates analogously to the previous get() member function, but delim is removed from the stream if it is actually encountered. At most len - 1 bytes are written into the buffer, and a trailing ASCII-Z character is appended to the string that was read. The delimiter itself is not stored in the buffer. If delim was not found (before reading len - 1 characters) the fail() member function, and possibly also eof() will return true.
  • istream& istream::ignore(int n , int delim):
    This member function has two (optional) arguments. When called without arguments, one character is skipped from the input stream. When called with one argument, n characters are skipped. The optional second argument specifies a delimiter: after skipping n or the delim character (whichever comes first) the function returns.
  • int istream::peek():
    this function returns the next available input character, but does not actually remove the character from the input stream.
  • istream& istream::putback (char c):
    The character c that was last read from the stream is `pushed back' into the input stream, to be read again as the next character. EOF is returned if this is not allowed. Normally, one character may always be put back. Note that c must be the character that was last read from the stream. Trying to put back any other character will fail.
  • istream& istream::read(char *buffer, int len):
    This function reads at most len bytes from the input stream into the buffer. If EOF is encountered first, fewer bytes are read, and the member function eof() will return true. This function will normally be used for reading binary files. Section 5.5.2 contains an example in which this member function is used. The member function gcount() should be used to determine the number of characters that were retrieved by the read() member function.
  • istream& istream::readsome(char *buffer, int len):
    This function reads at most len bytes from the input stream into the buffer. All available characters are read into the buffer, but if EOF is encountered first, fewer bytes are read, without setting the ios_base::eofbit or ios_base::failbit.
  • istream& istream::unget():
    an attempt is made to push back the last character that was read into the stream. Normally, this succeeds if requested only once after a read operation, as is the case with putback()
  • 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:

  • pos_type istream::tellg():
    this function returns the current (absolute) position where the next write-operation to the stream will take place. For all practical purposes a pos_type can be considered to be an unsigned long.
  • istream &istream::seekg(off_type step, ios::seekdir org):
    This member function can be used to reposition the stream. The function expects a off_type step, the stepsize in bytes to go from org. For all practical purposes a pos_type can be considered to be a long. The origin of the step, org is a ios::seekdir value. Possible values are:
  • ios::beg:
    org is interpreted as the stepsize relative to the beginning of the stream. If org is not specified, ios::beg is used.
  • ios::cur:
    org is interpreted as the stepsize relative to the current position (as returned by tellg() of the stream).
  • ios::end:
    org is interpreted as the stepsize relative to the current end position of the the stream.
  • While it is allowed to seek beyond end of file, reading at that point will of course fail. It is not allowed to seek before begin of file. Seeking before ios::beg will cause the ios::fail flag to be set.
  • 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:

  • ifstream object:
    This is the basic constructor. It creates an ifstream object which may be associated with an actual file later, using the open() member (see below).
  • ifstream object(char const *name, int mode):
    This constructor can be used to associate an ifstream object with the file named name, using input mode mode. The input mode is by default ios::in. See also section 5.4.2.1 for an overview of available file modes.

    In the following example an ifstream object is opened for reading. The file must exist:

        ifstream in("/tmp/scratch");
    
  • Instead of directly associating an ifstream object with a file, the object can be constructed first, and opened later.
  • tt(void ifstream::open(char const *name, int mode, int perm)):
    Having constructed an ifstream object, the member function open() can be used to associate the ifstream object with an actual file.
  • ifstream::close():
    Conversely, it is possible to close an ifstream object explicitly using the close() member function. The function sets the ios::fail flag of the closed object. A file is automatically closed when the associated ifstream object ceases to exist.
  • 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:
  • istringstream istr:
    The constructor will construct an empty istringstream object. The object may be filled with information to be retrieved later.
  • istringstream istr(string const &text):
    The constructor will construct an istringstream object initialized with the contents of the string text.
  • void istringstream::str(string const &text):
    This member function will store the contents of the string text into the istringstream object, overwriting its current contents.
  • 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:
        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 paramterless. Sources in which manipulators expecting arguments are used must

        #include <iomanip>
    

  • ios::boolalpha:
    This manipulator will set the ios::boolalpha flag.
  • ios::dec:
    This manipulator enforces the display and reading of integral numbers in decimal format. This is the default conversion. The conversion is applied to values inserted into the stream after processing the manipulators. For example (see also ios::hex and ios::oct, below):
        cout << 16 << ", " << hex << 16 << ", " << oct << 16;
        // produce the output:
        16, 10, 20
    
  • ios::endl:
    This manipulator will insert a newline character into an output buffer and will flush the buffer thereafter.
  • ios::ends:
    This manipulator will insert a string termination character into an output buffer.
  • ios::fixed:
    This manipulator will set the ios::fixed flag.
  • ios::flush:
    This manipulator will flush an output buffer.
  • ios::hex:
    This manipulator enforces the display and reading of integral numbers in hexadecimal format.
  • ios::internal:
    This manipulator will set the ios::internal flag.
  • ios::left:
    This manipulator will align values to the left in wide fields.
  • ios::noboolalpha:
    This manipulator will clear the ios::boolalpha flag.
  • ios::noshowpoint:
    This manipulator will clear the ios::showpoint flag.
  • ios::noshowpos:
    This manipulator will clear the ios::showpos flag.
  • ios::noshowbase:
    This manipulator will clear the ios::showbase flag.
  • ios::noskipws:
    This manipulator will clear the ios::skipws flag.
  • ios::nounitbuf:
    This manipulator will stop flushing an output stream after each write operation. Now the stream is flushed at a flush, endl, unitbuf or when it is closed.
  • ios::nouppercase:
    This manipulator will clear the ios::uppercase flag.
  • ios::oct:
    This manipulator enforces the display and reading of integral numbers in octal format.
  • ios::resetiosflags(flags):
    This manipulator calls ios::resetf(flags) to clear the indicated flag values.
  • ios::right:
    This manipulator will align values to the right in wide fields.
  • ios::scientific:
    This manipulator will set the ios::scientific flag.
  • ios::setbase(int b):
    This manipulator can be used to display integral values using the base 8, 10 or 16. It can be used as an alternative to oct, dec, hex in situations where the base of integral values is parameterized.
  • ios::setfill(int ch):
    This manipulator defines the filling character in situations where the values of numbers are too small to fill the width that is used to display these values. By default the blank space is used.
  • ios::setiosflags(flags):
    This manipulator calls ios::setf(flags) to set the indicated flag values.
  • ios::setprecision(int width):
    This manipulator will set the precision in which a float or double is displayed.
  • ios::setw(int width):
    This manipulator expects as its argument the width of the field that is inserted or extracted next. It can be used as manipulator for insertion, where it defines the maximum number of characters that are displayed for the field, but it can also be used during extraction, where it defines the maximum number of characters that are inserted into an array of characters. To prevent array bounds overflow when extracting from cin, setw() can be used as well:
    cin >> setw(sizeof(array)) >> array;

    A nice feature is that a long string appearing at cin is split into substrings of at most sizeof(array) - 1 characters, and that an ASCII-Z character is automatically appended. Notes:

  • setw() is valid only for the next field. It does not act like e.g., hex which changes the general state of the output stream for displaying numbers.
  • When setw(sizeof(someArray)) is used, make sure that someArray really is an array, and not a pointer to an array: the size of a pointer, being, e.g., four bytes, is usually not the size of the array that it points to....
  • ios::showbase:
    This manipulator will set the ios::showbase flag.
  • ios::showpoint:
    This manipulator will set the ios::showpoint flag.
  • ios::showpos:
    This manipulator will set the ios::showpos flag.
  • ios::skipws:
    This manipulator will set the ios::skipws flag.
  • ios::unitbuf:
    This manipulator will flush an output stream after each write operation.
  • ios::uppercase:
    This manipulator will set the ios::uppercase flag.
  • ios::ws:
    This manipulator will remove all whitespace characters that are available at the current read-position of an input buffer.
  • 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++. Alsthough the class cannot be used directly, its members are intriduced here, as the current chapter is the most logical place to introduce the class streambuf.

    The primary reason for existense 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, and it is implemented, e.g., in 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:

  • streamsize streambuf::in_avail():
    This member function returns a lower bound on the number of characters that can be read immediately.
  • int streambuf::sbumpc():
    This member function returns the next available character or EOF. The character is removed from the streambuf object. If no input is available, sbumpc() will call the (protected) member uflow() (see section 5.7.1 below) to make new characters available. EOF is returnedt if no more characters are available.
  • int streambuf::sgetc():
    This member function returns the next available character or EOF. The character is not removed from the streambuf object, however.
  • int streambuf::sgetn(char *buffer, streamsize n):
    This member function reads n characters from the input buffer, and stores them in buffer. The actual number of characters read is returned. This member function calls the (protected) member xsgetn() (see section 5.7.1 below) to obtain the requested number of characters.
  • int streambuf::snextc():
    This member function removes the current character from the input buffer and returns the next available character or EOF. The character is not removed from the streambuf object, however.
  • int streambuf::sputback(char c):
    Inserts c as the next character to read from the streambuffer object. Caution should be exercised when using this function: often at most one character can be put back.
  • int streambuf::sungetc():
    Returns the last character read to the input buffer, to be read again at the next input operation. Caution should be exercised when using this function: often at most one character can be put back.
  • Public members for output operations:

  • int streambuf::pubsync():
    Synchronize (i.e., flush) the buffer, by writing any pending information available in the streambuf's buffer to the device. Normally used only by specializing classes.
  • int streambuf::sputc(char c):
    This member function inserts c into the streambuffer object. If, after writing the character, the buffer is full, the function calls the (protected) member function overflow() to flush the buffer to the device (see section 5.7.1 below).
  • int streambuf::sputn(char const *buffer, streamsize n):
    This member function inserts n characters from buffer into the streambuffer object. The actual number of inserted characters is returned. This member function calls the (protected) member xsputn() (see section 5.7.1 below) to insert the requested number of characters.
  • Public members for miscellaneous operations:

  • pos_type streambuf::pubseekoff(off_type offset, ios::seekdir way, ios::openmode mode = ios::in | ios::out):
    Reset the offset of the next character to be read or written to offset, relative to the standard ios::seekdir values indicating the direction of the seeking operation. Normally used only by specializing classes.
  • pos_type streambuf::pubseekpos(pos_type offset, ios::openmode mode = ios::in | ios::out):
    Reset the absolute position of the next character to be read or written to pos. Normally used only by specializing classes.
  • streambuf *streambuf::pubsetbuf(char* buffer, streamsize n):
    Define buffer as the buffer to be used by the streambuf object. Normally used only by specializing classes.
  • 5.7.1: Protected `streambuf' members

    The protected members of the class streambuf are not normally accessible. However, they may be made accessible by specializing classes, and 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, using its data members directly 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. Below this figure is referred to repeatedly.

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


    Protected constructor:

  • streambuf::streambuf():
    Default (protected) constructor of the class streambuf.
  • Protected member functions related to input operations:

  • char *streambuf::eback():
    For the input buffer the class streambuf maintains three pointers: eback() points to the `end of the putback' area: characters can safely be put back up to this position. See also figure 4. Eback() can be considered to represent the beginning of the input buffer.
  • char *streambuf::egptr():
    For the input buffer the class streambuf maintains three pointers: egptr() points just beyond the last character that can be retrieved. See also figure 4. If gptr() (see below) equals egptr() the buffer must be refilled. This is realized by calling underflow(), see below.
  • void streambuf::gbump(int n):
    This function moves the input pointer over n positions.
  • char *streambuf::gptr():
    For the input buffer the class streambuf maintains three pointers: gptr() points to the next character to be retrieved. See also figure 4.
  • int streambuf::pbackfail(int c):
    This member function may be redefined by specializations of the class streambuf to do something intelligent when putting back character c fails. One of the things to consider here is to restore the old read pointer when putting back a character fails, because the beginning of the input buffer is reached. This member function is called when ungetting or putting back a character fails.
  • void streambuf::setg(char *beg, char *next, char *beyond):
    This member function initializes an input buffer: beg points to the beginning of the input area, next points to the next character to be retrieved, and beyond points beyond the last character of the input buffer. Ususally next is at least beg + 1, to allow for a put back operation. No input buffering is used when this member is called with 0-arguments.
  • streamsize streambuf::showmanyc():
    (Pronounce: s-how-many-c) This member function may be redefined by specializations of the class streambuf. It must return a guaranteed lower bound on the number of characters that can be read from the device before uflow() or underflow() returns EOF. By default 0 is returned (meaning at least 0 characters will be returned before the latter two functions will return EOF).
  • int streambuf::uflow():
    This member function may be redefined by specializations of the class streambuf to reload an input buffer with new characters. The default implementation is to call underflow(), see below, and to increment the read pointer gptr().
  • int streambuf::underflow():
    This member function may be redefined by specializations of the class streambuf to read another character from the device. The default implementation is to return EOF. When buffering is used, often the complete buffer is not refreshed, as this would make it impossible to put back characters just after a reload. This system, where only a subsection of the input buffer is reloaded, is called a split buffer.
  • streamsize streambuf::xsgetn(char *buffer, streamsize n):
    This member function may be redefined by specializations of the class streambuf to retrieve n characters from the device. The default implementation is to call sbumpc() for every single character. By default this calls (eventually) underflow() for every single character.
  • Protected member functions related to output operations:

  • int streambuf::overflow(int c):
    This member function may be redefined by specializations of the class streambuf to flush the characters in the output buffer to the device, and then to reset the output buffer pointers such that the buffer may be considered empty. If no output buffering is used, overflow() is called for every single character which is written to the streambuf object. This is realized by setting the buffer pointers (using, e.g., setp(), see below) to 0. The default implementation returns EOF, indicating that no characters can be written to the device.
  • char *streambuf::pbase():
    For the output buffer the class streambuf maintains three pointers: pbase() points to the beginning of the output buffer area. See also figure 4.
  • char *streambuf::epptr():
    For the output buffer the class streambuf maintains three pointers: epptr() points just beyond the location of the last character that can be written. See also figure 4. If pptr() (see below) equals epptr() the buffer must be flushed. This is realized by calling overflow(), see below.
  • void streambuf::pbump(int n):
    This function moves the output pointer over n positions.
  • char *streambuf::pptr():
    For the output buffer the class streambuf maintains three pointers: pptr() points to the location of the next character to be written. See also figure 4.
  • void streambuf::setp(char *beg, char *beyond):
    This member function initializes an output buffer: beg points to the beginning of the output area and beyond points beyond the last character of the output area. Use 0 for the arguments to indicate that no buffering is requested. In that case overflow() is called for every single character to write to the device.
  • streamsize streambuf::xsputn(char const *buffer, streamsize n):
    This member function may be redefined by specializations of the class streambuf to write n characters immediately to the device. The default implementation calls sputc() for each individual character, so redefining is only needed if a more efficient implementation is required.
  • Protected member functions related to buffer management and positioning:

  • streambuf *streambuf::setbuf(char *buffer, streamsize n):
    This member function may be redefined by specializations of the class streambuf to install a buffer. The default implementation is to do nothing.
  • pos_type streambuf::seekoff(off_type offset, ios::seekdir way, ios::openmode mode = ios::in | ios::out):
    This member function may be redefined by specializations of the class streambuf to reset the next pointer for input or output to a new relative position. The default implementation is to indicate failure by returning -1.
  • pos_type streambuf::seekpos(pos_type offset, ios::openmode mode = ios::in | ios::out):
    This member function may be redefined by specializations of the class streambuf to reset the next pointer for input or output to a new absolute position. The default implementation is to indicate failure by returning -1.
  • int sync():
    This member function may be redefined by specializations of the class streambuf to flush the output buffer to the device or to reset the input device to the position of the last consumed character. The default implementation (not using a buffer) is to return 0, indicatig successfull syncing. The member function is used to make sure that any characters that are still buffered are written to the device or to restore unconsumed characters to the device when the streambuf object ceases to exist.
  • 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:
  • filebuf::filebuf():
    Since the class has a constructor, it is, different from the class streambuf, possible to construct a filebuf object. This defines a plain filebuf object, not yet connected to a stream.
  • filebuf(int fd, char const *name, ios::openmode mode):
    This constructor is a non standard extension of the ANSI/ISO standard. It is mentioned here nonetheless since it may be expected that this or a comparable extension will be implemented in the next update of the standard. The argument fd is a file descriptor, and name is the name to be used for the file whose file descriptor is provided.
  • bool filebuf::is_open():
    This member function returns true if the filebuf is actually connected to an open file. See the open() member, below.
  • filebuf *filebuf::open(char const *name, ios::openmode mode):
    This member function associates the filebuf object with a file whose name is provided. The file is opened according to the provided ios::openmode.
  • filebuf *filebuf::close():
    This member function closes the association between the filebuf object and its file. The association is automatically closed when the filebuf object ceases to exist.
  • 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:
  • In an eternal loop:
    1. read a character
    2. if reading did not succeed (i.e., fail() returns true), break from the loop
    3. process the character
  • 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
        */
    
  • At lines 8-11 local variables are defined: errlog is the ofstream to write the error messages too, and cerr_buffer is a pointer to a streambuf, to point to the original cerr buffer. This is further discussed below.
  • At line 15 the alternate error stream is opened.
  • At line 16 the redirection takes place: cerr will now write to the streambuf defined by errlog. It is important that the original buffer used by cerr is saved, as explained below.
  • At line 29 we pause. At this point, two lines were written to the alternate error file. We get a chance to take a look at its contents: there were indeed two lines written to the file.
  • At line 33 the redirection is terminated. This is very important, as the errlog object is destroyed at the end of main(). If cerr's buffer would not have been restored, then at that point cerr would refer to a non-existing streambuf object, which might produce unexpected results. It is the responsibility of the programmer to make sure that an original streambuf is saved before redirection, and is restored when the redirection ends.
  • Finally, at line 34, Done is now written to the screen again, as the redirection has been terminated.
  • 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.

    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 a 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:
  • The streams to be associated with the streambuf objects of existing streams are not ifstream or ofstream objects (or, for that matter, istringstream or ostringstream objects), but basic istream and ostream objects.
  • The streambuf object does not have to be defined in an ifstream or ofstream object: it can be defined outside of the streams, using constructions like:
        filebuf
            fb("index", ios::in | ios::out | ios::trunc);
        istream
            index_in(&fb);
        ostream
            index_out(&fb);
    
  • Note that an ifstream object can be constructed using stream modes normally used for writing to files. Conversely, ofstream objects can be constructed using stream modes normally used for reading from files.
  • If istream and ostreams are associated through a common streambuf, then the read and write pointers (should) point to the same locations: they are tightly coupled.
  • The advantage of using a separate streambuf over a predefined fstream object is (of course) that it opens the possibility of using stream objects with specialized streambuf objects. These streambuf objects may then specifically be constructed to interface particular devices. Elaborating this is left as an exercise to the reader.