We're always interested in getting feedback. E-mail us if you like this guide, if you think that important material is omitted, if you encounter errors in the code examples or in the documentation, if you find any typos, or generally just if you feel like e-mailing. Send your email to Frank Brokken.Please state the document version you're referring to, as found in the title (in this document: 5.2.0a) and please state the paragraph you're referring to.
All mail received is seriously considered, and new (sub)releases of the Annotations will normally reflect your suggestions for improvements. Except for the incidental case I will not otherwise acknowledge the receipt of suggestions for improvements. Please don't misinterpret this for lack of appreciation.
As an extension to the standard stream (
FILE
) approach well known from the
C programming language, C++ offers an input/output (
I/O)
library
based on class
concepts.
Earlier (in chapter 3) we've already seen examples of the
use of the C++ I/O library, especially the use of the insertion
operator (operator<<()
) and the extraction operator (operator>>()
).
In this chapter we'll cover the library in more detail.
The discussion of input and output facilities provided by the C++
programming language heavily uses the class
concept, and the notion of
member functions. Although the construction of classes will be covered in the
upcoming chapter 6, and inheritance only in chapter
13, we think it is well possible to introduce input and output
(I/O) facilities well before discussing the technical background of these
topics.
Most C++ I/O classes have names starting with
basic_
(like
basic_ios
). However, these
basic_
names are not regularly found in
C++ programs, as most classes are also defined using
typedef
definitions like:
typedef basic_ios<char> ios;
Since C++ defines both the
char
and
wchar_t
types, I/O
facilities were developed using the
template mechanism. As will be
further elaborated in chapter 18, this way it was possible to
construct
generic software, which could be used for both the char
and
wchar_t
types. So, analogously to the above typedef
there exists a
typedef basic_ios<wchar_t> wios;
This type definition can be used for the wchar_t
type. Due to the
type definitions, the basic_
prefix can be omitted in the Annotations. In
the Annotations the emphasis is primarily on the standard 8-bits char
type.
As a side effect to this implementation it must be stressed that it is not correct anymore to declare iostream objects using standard forward declarations, like:
class ostream; // now erroneousInstead, sources that must declare iostream classes must
#include <iosfwd> // correct way to declare iostream classes
Using the C++ I/O library offers the additional advantage of
type safety. Objects (or plain values) are inserted into
streams. Compare this to the situation commonly encountered in C where the
fprintf()
function is used to indicate by a format string what kind of
value to expect where. Compared to this latter situation C++'s
iostream approach immediately uses the objects where their values should
appear, as in
cout << "There were " << nMaidens << " virgins present\n";
The compiler notices the type of the nMaidens
variable, inserting
its proper value at the appropriate place in the sentence inserted into
the cout
iostream.
Compare this to the situation encountered in C. Although C compilers
are getting smarter and smarter over the years, and although a well-designed
C compiler may warn you for a mismatch between a format specifier and the
type of a variable encountered in the corresponding position of the argument
list of a
printf()
statement, it can't do much more than warn you.
The type safety seen in C++ prevents you from making type
mismatches, as there are no types to match.
Apart from this, iostreams offer more or less the same set of
possibilities as the standard FILE
-based I/O used in C: files can be
opened, closed, positioned, read, written, etc.. In C++ the basic FILE
structure, as used in C is still available. C++ adds I/O based on
classes to FILE
-based I/O, resulting in type safety, extensibility, and a
clean design. In the
ANSI/ISO standard the intent was to construct
architecture independent I/O. Previous implementations of the iostreams
library did not always comply with the standard, resulting in many extensions
to the standard. Software developed earlier may have to be partially rewritten
with respect to I/O. This is tough for those who are now forced to modify
existing software, but every feature and extension that was available in
previous implementations can be reconstructed easily using the ANSI/ISO
standard conforming I/O library. Not all of these reimplementations
can be covered in this chapter, as most use inheritance and polymorphism,
topics that will be covered in chapters 13 and
14, respectively. Selected reimplementations will be provided
in chapter 19, and below references to particular sections in that
chapter will be given where appropriate.
This chapter is organized as follows (see also figure 3):
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.
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.
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.
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.
filebuf
ojects. Other I/O
related topics are covered later in the Annotations, e.g., in chapter
19.
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.
#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 manipulators. See section 5.6
ios_base
forms the foundation of all I/O operations, and
defines, among other things, the facilities for inspecting the
state of I/O streams and most
output formatting facilities. Every
stream class of the I/O library is, via the class
ios
, derived from
this class, and inherits its capabilities.
The discussion of the class ios_base
precedes the introduction of members
that can be used for actual reading from and writing to streams. But as the
ios_base
class is the foundation on which all I/O in C++ was built, we
introduce it as the first class of the C++ I/O library.
Note, however, that as in C, I/O in C++ is not part of the
language (although it is part of the
ANSI/ISO standard on C++):
although it is technically possible to ignore all predefined I/O facilities,
nobody actually does so, and the I/O library represents therefore a
de facto I/O standard in C++. Also note that, as mentioned before,
the iostream classes do not do input and output themselves, but delegate this
to an auxiliary class: the class
streambuf
or its derivatives.
For the sake of completeness and not so much because it is necessary to
understand the ongoing discussion, it is noted here that it is not
possible to construct an ios_base
object directly. As covered by chapter
13, classes that are derived from ios_base
(like ios
)
may construct ios_base
objects using the
ios_base::ios_base()
constructor.
The next class in the iostream hierarchy (see figure 3) is the
class
ios
. Since the stream classes inherit from the class ios
, and
thus also from ios_base
, in practice the distinction between ios_base
and
ios
is hardly important. Therefore, in the sequel facilities actually
provided by ios_base
are discussed as facilities provided by ios
. The
reader who is interested in the true class in which a particular facility is
defined should consult the relevant header files (e.g.,
ios_base.h
and
basic_ios.h
).
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 thestreambuf
object forming the interface between theios
object and the device with which theios
object communicates. See section 19.1.2 for further information about theclass streambuf
.
streambuf *ios::rdbuf(streambuf *new)
:
This member function can be used to associate aios
object with anotherstreambuf
object. A pointer to theios
object's originalstreambuf
object is returned. The object to which this pointer points is not destroyed when thestream
object goes out of scope, but is owned by the caller ofrdbuf()
.
ostream *ios::tie()
:This member function returns a pointer to theostream
object that is currently tied to theios
object. The returnedostream
object is flushed every time before information is input or output to theios
object of which thetie()
member is called. The return value 0 indicates that currently noostream
object is tied to theios
object. See section 5.8.2 for details.
ostream *ios::tie(ostream *new)
:
This member function can be used to associate anios
object with anotherostream
object. A pointer to theios
object's originalostream
object is returned. See section 5.8.2 for details.
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 theios
object. Theios::failbit
andios::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 whenios::eof()
orios::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, thenclear()
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 anios
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 memberios::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.
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).
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
orios::oct
).
ios::boolalpha
:
to display boolean values as text, using the text ``true
'' for thetrue
logical value, and the string ``false
'' for thefalse
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. Withsetf()
the mask valueios::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. Withsetf()
the mask valueios::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
orios::scientific
).
ios::hex
:
to read and display integral values as hexadecimal values (i.e., radix 16) values. Withsetf()
the mask valueios::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. Withsetf()
the mask valueadjustfield
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). Withsetf()
the mask valueadjustfield
must be provided. Corresponding manipulator:ios::left
.
ios::oct
:
to display integral values as octal values (i.e., radix 8) values. Withsetf()
the mask valueios::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. Withsetf()
the mask valueadjustfield
must be provided. Corresponding manipulator:ios::right
.
ios::scientific
:
to display real values in scientific notation (e.g., 1.24e+03). Withsetf()
the mask valueios::floatfield
must be provided. Corresponding manipulator:ios::scientific
.
ios::showbase
:
to display the numeric base of integral values. With hexadecimal values the0x
prefix is used, with octal values the prefix0
. For the (default) decimal value no particular prefix is used. Corresponding manipulators:ios::showbase
andios::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. Ifios::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 set (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 ©fmt(ios &obj)
:This member function copies all format definitions fromobj
to the currentios
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 (aschar
) 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 (asint
) 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 bitwiseoperator|()
to combine multiple flags. Other flags are not affected). Corresponding manipulators:ios::setiosflags
andios::resetiosflags
ios::setf(fmtflags flags, fmtflags mask)
:
returns the previous set of all flags, clears all flags mentioned inmask
, and sets the flags specified inflags
. Well-known mask values areios::adjustfield
,ios::basefield
andios::floatfield
. For example:
setf(ios::left, ios::adjustfield)
is used to left-adjust wide values in their field. (alternatively,ios::right
andios::internal
can be used).setf(ios::hex, ios::basefield)
is used to activate the hexadecimal representation of integral values (alternatively,ios::dec
andios::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 (asint
) 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 (asint
) the previously used output field width, redefines the value tonchars
for the next insertion operation. Note that the field width is reset to 0 after every insertion operation, and thatwidth()
currently has no effect on text-values likechar *
orstring
values.
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:
ostream
, offering the basic facilities for doing output;
ofstream
, allowing us to open files for writing
(comparable to C's
fopen(filename, "w")
);
ostringstream
, allowing us to write informatioon to
memory rather than to files (streams) (comparable to C's
sprintf()
function).
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.
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 mostlen
bytes, stored in thechar const *buffer
to theostream
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 apos_type
can be considered to be anunsigned long
.
ostream &ostream::seekp(off_type step, ios::seekdir org)
:
This member function can be used to reposition the stream. The function expects anoff_type
step
, the stepsize in bytes to go fromorg
. For all practical purposes aoff_type
can be considered to be along
. The origin of the step,org
is anios::seekdir
value. Possible values are:It is allowed to seek beyond end of file . Writing bytes to a location beyond
ios::beg
:org
is interpreted as the stepsize relative to the beginning of the stream. Iforg
is not specified,ios::beg
is used.ios::cur
:org
is interpreted as the stepsize relative to the current position (as returned bytellp()
of the stream).ios::end
:org
is interpreted as the stepsize relative to the current end position of the the stream.EOF
will pad the intermediate bytes with ASCII-Z values: null-bytes. It is not allowed to seek before begin of file. Seeking beforeios::beg
will cause theios::fail
flag to be set.
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 theostream
object. The call toflush()
is implied when:
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 anofstream
object which may be associated with an actual file later, using theopen()
member (see below).
ofstream object(char const *name, int mode)
:
This constructor can be used to associate anofstream
object with the file namedname
, using output modemode
. The output mode is by defaultios::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");
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 anofstream
object, the member functionopen()
can be used to associate theofstream
object with an actual file.
ofstream::close()
:
Conversely, it is possible to close anofstream
object explicitly using theclose()
member function. The function sets theios::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 associatedofstream
object ceases to exist.
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.
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.
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 anopenmode
parameter. Ifstring s
is specified andopenmode
isios::ate
, theostringstream
object is initialized with thestring s
and remaining insertions are appended to the contents of theostringstream
object. Ifstring 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 theostringstream
object goes out of scope.
string ostringstream::str() const
:
This member function will return the string that is stored
inside the ostringstream
object.
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 */
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:
istream
, offering the basic facilities for doing input;
ifstream
, allowing us to open files for reading
(comparable to C's
fopen(filename, "r")
);
istringstream
, allowing us to read informatioon from
text that is not stored on files (streams) but in memory (comparable to
C's
sscanf()
function).
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.
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 returnsEOF
or reads and returns the next available single character as anint
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 oflen - 1
characters from the input stream into the array starting atbuffer
, which should be at leastlen
bytes long. At mostlen - 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
, anASCII-Z
character is written beyond the last character that was written tobuffer
. The functionseof()
andfail()
(see section 5.3.1) return 0 (false
) if the delimiter was not encountered beforelen - 1
characters were read. Furthermore, anASCII-Z
can be used for the delimiter: this way strings terminating inASCII-Z
characters may be read from a (binary) file. The program using thisget()
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 previousget()
member function, butdelim
is removed from the stream if it is actually encountered. At mostlen - 1
bytes are written into thebuffer
, and a trailingASCII-Z
character is appended to the string that was read. The delimiter itself is not stored in thebuffer
. Ifdelim
was not found (before readinglen - 1
characters) thefail()
member function, and possibly alsoeof()
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 skippingn
or thedelim
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 characterc
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 thatc
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 mostlen
bytes from the input stream into the buffer. IfEOF
is encountered first, fewer bytes are read, and the member functioneof()
will returntrue
. 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 functiongcount()
should be used to determine the number of characters that were retrieved by theread()
member function.
istream& istream::readsome(char *buffer, int len)
:
This function reads at mostlen
bytes from the input stream into the buffer. All available characters are read into the buffer, but ifEOF
is encountered first, fewer bytes are read, without setting theios_base::eofbit
orios_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 apos_type
can be considered to be anunsigned long
.
istream &istream::seekg(off_type step, ios::seekdir org)
:This member function can be used to reposition the stream. The function expects aoff_type
step
, the stepsize in bytes to go fromorg
. For all practical purposes apos_type
can be considered to be along
. The origin of the step,org
is aios::seekdir
value. Possible values are: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
:org
is interpreted as the stepsize relative to the beginning of the stream. Iforg
is not specified,ios::beg
is used.ios::cur
:org
is interpreted as the stepsize relative to the current position (as returned bytellg()
of the stream).ios::end
:org
is interpreted as the stepsize relative to the current end position of the the stream.ios::beg
will cause theios::fail
flag to be set.
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 anifstream
object which may be associated with an actual file later, using theopen()
member (see below).
ifstream object(char const *name, int mode)
:
This constructor can be used to associate anifstream
object with the file namedname
, using input modemode
. The input mode is by defaultios::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");
ifstream
object with a file, the
object can be constructed first, and opened later.
void ifstream::open(char const *name, int mode, int perm)
:
Having constructed anifstream
object, the member functionopen()
can be used to associate theifstream
object with an actual file.
ifstream::close()
:
Conversely, it is possible to close anifstream
object explicitly using theclose()
member function. The function sets theios::fail
flag of the closed object. A file is automatically closed when the associatedifstream
object ceases to exist.
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)); }
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 anistringstream
object initialized with the contents of the stringtext
.
void istringstream::str(string const &text)
:
This member function will store the contents of the stringtext
into theistringstream
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: 3 123 666 */
Ios
objects define a set of format flags that are used for determining
the way values are inserted (see section 5.3.2.1). The format flags
can be controlled by member functions (see section 5.3.2.2), but
also by
manipulators. Manipulators are inserted into output streams
or extracted from input streams, instead of being activated through the member
selection operator (`.
').
Manipulators are functions. New manipulators can be constructed as well. The construction of manipulators is covered in section 9.9.1. In this section the manipulators that are available in the C++ I/O library are discussed. Most manipulators affect format flags. See section 5.3.2.1 for details about these flags. Most manipulators are parameterless. Sources in which manipulators expecting arguments are used, must do:
#include <iomanip>
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 alsoios::hex
andios::oct
, below):cout << 16 << ", " << hex << 16 << ", " << oct << 16; // produces 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 aflush
,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 afloat
ordouble
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 fromcin
,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 mostsizeof(array) - 1
characters, and that anASCII-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 thatsomeArray
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.
streambuf
defines the input and output character sequences that
are processed by streams. Like an
ios
object, a streambuf
object is
not directly constructed, but is implied by objects of other classes that are
specializations of the class streambuf
.
The class has an important role in realizing possibilities that were
available as extensions to the pre-
ANSI/ISO standard implementations of
C++. Although the class cannot be used directly, its members are
introduced here, as the current chapter is the most logical place to introduce
the class streambuf
.
The primary reason for existence of the class streambuf
, however, is
to decouple the stream
classes from the
devices they operate
upon. The rationale here is to use an extra software layer between on the one
hand the classes allowing us to communicate with the device and the
communication between the software and the devices themselves. This implements
a
chain of command which is seen regularly in
software design: The
chain of command is considered a generic pattern for the construction of
reusable software, encountered also in, e.g., the
TCP/IP stack. A
streambuf
can be considered yet another example of the chain of command
pattern: here the program talks to stream
objects, which in turn forward
their requests to streambuffer
objects, which in turn communicate with the
devices. Thus, as we will see shortly, we are now able to do in user-software
what had to be done via (expensive) system calls before.
The class streambuf
has no public constructor, but does make available
several public member functions. In addition to these public member functions,
several member functions are available to specializing classes only. These
protected members are listed in this section for further
reference. In section 5.7.2 below, a particular specialization of the
class streambuf
is introduced. Note that all public members of
streambuf
discussed here are also available in filebuf
.
In section 14.6 the process of constructing specializations
of the class streambuf
is discussed, and in chapter 19 several
other implications of using streambuf
objects are mentioned. In the
current chapter examples of copying streams, of redirecting streams and and of
reading and writing to streams using the streambuf
members of stream
objects are presented (section 5.8).
With the class streambuf
the following public member functions are
available. The type
streamsize
that is used below may, for all practical
purposes, be considered an unsigned int
.
Public members for input operations:
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 orEOF
. The character is removed from thestreambuf
object. If no input is available,sbumpc()
will call the (protected) memberuflow()
(see section 5.7.1 below) to make new characters available.EOF
is returned if no more characters are available.
int streambuf::sgetc()
:This member function returns the next available character orEOF
. The character is not removed from thestreambuf
object, however.
int streambuf::sgetn(char *buffer, streamsize n)
:This member function readsn
characters from the input buffer, and stores them inbuffer
. The actual number of characters read is returned. This member function calls the (protected) memberxsgetn()
(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 orEOF
. The character is not removed from thestreambuf
object, however.
int streambuf::sputback(char c)
:Insertsc
as the next character to read from thestreambuffer
object. Caution should be exercised when using this function: often there is a maximum of just one character that 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 there is a maximum of just one character that 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 insertsc
into thestreambuffer
object. If, after writing the character, the buffer is full, the function calls the (protected) member functionoverflow()
to flush the buffer to the device (see section 5.7.1 below).
int streambuf::sputn(char const *buffer, streamsize n)
:This member function insertsn
characters frombuffer
into thestreambuffer
object. The actual number of inserted characters is returned. This member function calls the (protected) memberxsputn()
(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 tooffset
, relative to the standardios::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)
:Definebuffer
as the buffer to be used by thestreambuf
object. Normally used only by specializing classes.
class
streambuf
are normally not
accessible. However, they are accessible in specializing classes which are
derived from streambuf
. They are important for understanding and using
the class streambuf
. Usually there are both protected
data members and
protected
member functions defined in the class streambuf
. Since using
data members immediately violates the principle of
encapsulation, these
members are not mentioned here. As the functionality of streambuf
, made
available via its member functions, is quite extensive, directly using its
data members is probably hardly ever necessary. This section not even lists
all protected member functions of the class streambuf
. Only those member
functions are mentioned that are useful in constructing specializations. The
class streambuf
maintains an input- and/or and output buffer, for which
beginning, actual and ending pointers are defined, as depicted in figure
4. In the sequel we will refer to this figure repeatedly.
Protected member functions related to input operations:
char *streambuf::eback()
:For the input buffer theclass 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 theclass streambuf
maintains three pointers:egptr()
points just beyond the last character that can be retrieved. See also figure 4. Ifgptr()
(see below) equalsegptr()
the buffer must be refilled. This is realized by callingunderflow()
, see below.
void streambuf::gbump(int n)
:This function moves the input pointer over n
positions.
char *streambuf::gptr()
:For the input buffer theclass 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 theclass streambuf
to do something intelligent when putting back characterc
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, andbeyond
points beyond the last character of the input buffer. Ususallynext
is at leastbeg + 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 theclass streambuf
. It must return a guaranteed lower bound on the number of characters that can be read from the device beforeuflow()
orunderflow()
returnsEOF
. By default 0 is returned (meaning at least 0 characters will be returned before the latter two functions will returnEOF
).
int streambuf::uflow()
:This member function may be redefined by specializations of theclass streambuf
to reload an input buffer with new characters. The default implementation is to callunderflow()
, see below, and to increment the read pointergptr()
.
int streambuf::underflow()
:This member function may be redefined by specializations of theclass streambuf
to read another character from the device. The default implementation is to returnEOF
. 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 theclass streambuf
to retrieven
characters from the device. The default implementation is to callsbumpc()
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 theclass 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 thestreambuf
object. This is realized by setting the buffer pointers (using, e.g.,setp()
, see below) to 0. The default implementation returnsEOF
, indicating that no characters can be written to the device.
char *streambuf::pbase()
:For the output buffer theclass 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 theclass streambuf
maintains three pointers:epptr()
points just beyond the location of the last character that can be written. See also figure 4. Ifpptr()
(see below) equalsepptr()
the buffer must be flushed. This is realized by callingoverflow()
, see below.
void streambuf::pbump(int n)
:This function moves the output pointer over n
positions.
char *streambuf::pptr()
:For the output buffer theclass 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 andbeyond
points beyond the last character of the output area. Use 0 for the arguments to indicate that no buffering is requested. In that caseoverflow()
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 theclass streambuf
to writen
characters immediately to the device. The default implementation callssputc()
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 theclass 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 thestreambuf
object ceases to exist.
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.
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 theclass streambuf
, possible to construct afilebuf
object. This defines a plainfilebuf
object, not yet connected to a stream.
bool filebuf::is_open()
:This member function returnstrue
if thefilebuf
is actually connected to an open file. See theopen()
member, below.
filebuf *filebuf::open(char const *name, ios::openmode mode)
:This member function associates thefilebuf
object with a file whose name is provided. The file is opened according to the providedios::openmode
.
filebuf *filebuf::close()
:This member function closes the association between thefilebuf
object and its file. The association is automatically closed when thefilebuf
object ceases to exist.
fail()
returns
true), break
from the loop
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.
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.
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.logWith 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.logHere 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 */
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.
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.
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.
Done
is now written to the screen again, as
the redirection has been terminated.
fstream
object
must be created. As with ifstream
and ofstream
objects, the
constructor receives the name of the file to be opened:
fstream inout("iofile", ios::in | ios::out);
Note the use of the
ios
constants
ios::in
and
ios::out
,
indicating that the file must be opened for both reading and writing. Multiple
mode indicators may be used, concatenated by the binary or operator '|'
.
Alternatively, instead of ios::out
,
ios::app
could have been used, in
which case writing will always be done at the end of the file.
Somehow reading and writing to a file is a bit awkward: what to do when the file may or may not exist yet, but if it already exists it should not be rewritten? I have been fighting with this problem for some time, and now use the following approach:
#include <fstream> #include <iostream> #include <string> using namespace std; int main() { fstream rw("fname", ios::out | ios::ate | ios::in); if (!rw) { rw.clear(); rw.open("fname", ios::out | ios::trunc | ios::in); } if (!rw) { cerr << "Opening `fname' failed miserably" << endl; return 1; } cerr << rw.tellp() << endl; rw << "Hello world" << endl; rw.seekg(0); string s; getline(rw, s); cout << "Read: " << s << endl; }In the above example, the constructor fails when
fname
doesn't exist
yet. However, in that case the open()
member succeeds. If the file already
exists, the constructor succeeds. Because of the
ios::ate
flag, the next
read/write action will by default take place at EOF
. However,
ios::ate
is not
ios::app
, and the stream object can be repositioned
using seekg()
or seekp()
.
Under
DOS-like operating
systems, which use the multiple character \r\n
sentinels to separate lines
in
text files the flag
ios::bin
is required for
processing
binary files to ensure that \r\n
combinations are processed
as two characters.
With fstream
objects, combinations of file flags are used to make sure
that a stream is or is not (re)created empty when opened. See section
5.4.2.1 for details.
Once a file has been opened in read and write mode, the <<
operator
can be used to insert information to the file, while the >>
operator may
be used to extract information from the file. These operations may be
performed in random order. The following fragment will read a blank-delimited
word from the file, and will then write a string to the file, just beyond the
point where the string just read terminated, followed by the reading of yet
another string just beyond the location where the string just written ended:
fstream f("filename", ios::in | ios::out | ios::creat); string str; f >> str; // read the first word // write a well known text f << "hello world"; f >> str; // and read againSince the operators
<<
and >>
can apparently be used with
fstream
objects, you might wonder whether a series of <<
and >>
operators in one statement might be possible. After all, f >> str
should produce an fstream &
, shouldn't it?
The answer is: it doesn't. The compiler casts the fstream
object into
an
ifstream
object in combination with the extraction operator, and into
an
ofstream
object in combination with the insertion
operator. Consequently, a statement like
f >> str << "grandpa" >> str;
results in a compiler error like
no match for `operator <<(class istream, char[8])'
Since the compiler complains about the istream
class, the fstream
object is apparently considered an ifstream
object in combination with the
extraction operator.
Of course, random insertions and extractions are hardly used. Generally,
insertions and extractions take place at specific locations in the file.
In those cases, the position where the insertion or extraction must take
place can be controlled and monitored by the
seekg()
and
tellg()
member functions (see sections 5.4.1.2 and 5.5.1.2).
Error conditions (see section 5.3.1) occurring due to, e.g., reading
beyond end of file, reaching end of file, or positioning before begin of file,
can be cleared using the
clear()
member function. Following clear()
processing may continue. E.g.,
fstream f("filename", ios::in | ios::out | ios::creat); string str; f.seekg(-10); // this fails, but... f.clear(); // processing f continues f >> str; // read the first wordA 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:
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.
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);
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.
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.
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.