Don't hesitate to send in feedback: send an e-mail if you like the C++ Annotations; if you think that important material was omitted; if you find errors or typos in the text or the code examples; or if you just feel like e-mailing. Send your e-mail to Frank B. Brokken.Please state the document version you're referring to, as found in the title (in this document: 10.3.0) and please state chapter and paragraph name or number you're referring to.
All received mail is processed conscientiously, and received suggestions for improvements are usually processed by the time a new version of the Annotations is released. Except for the incidental case I will normally not acknowledge the receipt of suggestions for improvements. Please don't interpret this as me not appreciating your efforts.
Extending the standard stream (FILE
) approach, well known from the
C programming language, C++ offers an input/output (I/O)
library based on class
concepts.
All C++ I/O facilities are defined in the namespace std
. The std::
prefix is omitted below, except for situations where this would result in
ambiguities.
Earlier (in chapter 3) we've seen several examples of the use of the C++ I/O library, in particular showing insertion operator (<<) and the extraction operator (>>). In this chapter we'll cover I/O in more detail.
The discussion of input and output facilities provided by the C++
programming language heavily uses the class
concept and the notion of
member functions. Although class construction has not yet been covered (for
that see chapter 7) and although inheritance is not covered
formally before chapter 13, it is quite possible to discuss I/O
facilities long before the technical background of class construction has been
covered.
Most C++ I/O classes have names starting with basic_
(like
basic_ios
). However, these basic_
names are not regularly found in
C++ programs, as most classes are also defined using typedef
definitions like:
typedef basic_ios<char> ios;Since C++ supports various kinds of character types (e.g.,
char
,
wchar_t
), I/O facilities were developed using the template mechanism
allowing for easy conversions to character types other than the traditional
char
type. As elaborated in chapter 21, this also allows the
construction of generic software, that could thereupon be used for any
particular type representing characters. So, analogously to the above
typedef
there exists a
typedef basic_ios<wchar_t> wios;This type definition can be used for the
wchar_t
type. Because of the
existence of these type definitions, the basic_
prefix was omitted from
the C++ Annotations without loss of continuity. The C++ Annotations primarily
focus on the standard 8-bits char
type.
Iostream objects cannot be declared using standard forward declarations, like:
class std::ostream; // now erroneousInstead, to declare iostream classes the
<iosfwd>
header file
should be included:
#include <iosfwd> // correct way to declare iostream classes
Using C++ I/O offers the additional advantage of
type safety. Objects (or plain values) are inserted into
streams. Compare this to the situation commonly encountered in C where the
fprintf
function is used to indicate by a format string what kind of
value to expect where. Compared to this latter situation C++'s
iostream approach immediately uses the objects where their values should
appear, as in
cout << "There were " << nMaidens << " virgins present\n";The compiler notices the type of the
nMaidens
variable, inserting
its proper value at the appropriate place in the sentence inserted into
the cout
iostream.
Compare this to the situation encountered in C. Although C compilers
are getting smarter and smarter, and although a well-designed
C compiler may warn you for a mismatch between a format specifier and the
type of a variable encountered in the corresponding position of the argument
list of a printf
statement, it can't do much more than warn you.
The type safety seen in C++ prevents you from making type
mismatches, as there are no types to match.
Apart from this, iostreams offer more or less the same set of
possibilities as the standard FILE
-based I/O used in C: files can be
opened, closed, positioned, read, written, etc.. In C++ the basic FILE
structure, as used in C, is still available. But C++ adds to this I/O
based on classes, resulting in type safety, extensibility, and a clean design.
In the ANSI/ISO standard the intent was to create architecture independent I/O. Previous implementations of the iostreams library did not always comply with the standard, resulting in many extensions to the standard. The I/O sections of previously developed software may have to be partially rewritten. This is tough for those who are now forced to modify old software, but every feature and extension that was once available can be rebuilt easily using ANSI/ISO standard conforming I/O. Not all of these reimplementations can be covered in this chapter, as many reimplementations rely on inheritance and polymorphism, which topics are formally covered by chapters 13 and 14. Selected reimplementations are provided in chapter 24, and in this chapter references to particular sections in other chapters are given where appropriate.
This chapter is organized as follows (see also Figure 3):class
ios_base
is the foundation upon which the
iostreams I/O library was built. It defines the core of all I/O operations and
offers, among other things, facilities for inspecting the
state of I/O streams and for output formatting.
ios
was directly derived from ios_base
. Every
class of the I/O library doing input or output is itself derived from this
ios
class, and so inherits its (and, by implication, ios_base
's)
capabilities. The reader is urged to keep this in mind while reading this
chapter. The concept of inheritance is not discussed here, but rather
in chapter 13.
The class ios
is important in that it implements the communication with a
buffer that is used by streams. This buffer is a streambuf
object
which is responsible for the actual I/O to/from the underlying
device. Consequently iostream
objects do not perform I/O
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 writing information to
streams. Apart from inserting information into files it is possible to insert
information into memory buffers, for which the ostringstream
class is
available. Formatting 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 into streams using
manipulators. This aspect of C++ output is
discussed as well.
istream
class. This class defines the extraction operator and related input
facilities. Comparably to inserting information into memory buffers (using
ostringstream
) a class istringstream
is available to extract
information from memory buffers.
filebuf
objects. Other I/O
related topics are covered elsewhere in the C++ Annotations, e.g., in chapter
24.
streambuf
, which is responsible for the actual input and output
to the device accessed by a streambuf
object.
This approach allows us to construct a new kind of streambuf
for a new
kind of device, and use that streambuf in combination with the `good old'
istream
- and ostream
-class facilities. It is important to understand
the distinction between the formatting roles of iostream objects and the
buffering interface to an external device as implemented in a streambuf
object. Interfacing to new devices (like sockets or
file descriptors) requires the construction of a new kind of
streambuf
, rather than a new kind of istream
or ostream
object. A
wrapper class may be constructed around the istream
or ostream
classes, though, to ease the access to a special device. This is how the
stringstream classes were constructed.
iosfwd
: sources should include this header file if only a declaration
of the stream classes is required. For example, if a function defines a
reference parameter to an ostream
then the compiler does not need to know
exactly what an ostream
is. When declaring such a function
the ostream
class merely needs to be be declared. One cannot use
class std::ostream; // erroneous declaration void someFunction(std::ostream &str);but, instead, one should use:
#include <iosfwd> // correctly declares class ostream void someFunction(std::ostream &str);
<ios>
: sources should include this header file when using
types and facilites (like ios::off_type
, see below) defined in the
ios
class.
<streambuf>
: sources should include this header file when using
streambuf
or filebuf
classes. See sections 14.8 and
14.8.2.
<istream>
: sources should include this preprocessor directive when
using the class istream
or when using classes that do both input and
output. See section 6.5.1.
<ostream>
: sources should include this header file when using the class
ostream
class or when using classes that do both input and output. See
section 6.4.1.
<iostream>
: sources should include this header file when using the
global stream objects (like cin
and cout
).
<fstream>
: sources should include this header file when using the file
stream classes. See sections 6.4.2, 6.5.2, and 6.6.3.
<sstream>
: sources should include this header file when using the
string stream classes. See sections 6.4.3 and 6.5.3.
<iomanip>
: sources should include this header file when using
parameterized manipulators. See section 6.3.2.
std::ios_base
forms the foundation of all I/O
operations, and defines, among other things, facilities for inspecting the
state of I/O streams and most output formatting facilities. Every
stream class of the I/O library is, through the class ios
, derived
from this class, and inherits its capabilities. As ios_base
is the
foundation on which all C++ I/O was built, we introduce it here as the
first class of the C++ I/O library.
Note that, as in C, I/O in C++ is not part of the language
(although it is part of the ANSI/ISO standard on C++). Although it
is technically possible to ignore all predefined I/O facilities, nobody does
so, and the I/O library therefore represents a de facto I/O standard for
C++. Also note that, as mentioned before, the iostream classes themselves
are not responsible for the eventual I/O, but delegate this to an auxiliary
class: the class streambuf
or its derivatives.
It is neither possible nor required to construct an ios_base
object
directly. Its construction is always a side-effect of constructing an object
further down the class hierarchy, like std::ios
. Ios
is the next
class down the iostream hierarchy (see figure 3). Since all
stream classes in turn inherit from ios
, and thus also from ios_base
,
the distinction between ios_base
and ios
is in practice not
important. Therefore, facilities actually provided by ios_base
will be
discussed as facilities provided by ios
. The reader who is interested in
the true class in which a particular facility is defined should consult the
relevant header files (e.g., ios_base.h
and basic_ios.h
).
std::ios
class is derived directly from ios_base
, and it
defines de facto the foundation for all stream classes of the C++ I/O
library.
Although it is possible to construct an ios
object
directly, this is seldom done. The purpose of the class ios
is to
provide the facilities of the class basic_ios
, and to add several new
facilites, all related to the streambuf
object which is managed
by objects of the class ios
.
All other stream classes are either directly or indirectly derived from
ios
. This implies, as explained in chapter 13, that all
facilities of the classes ios
and ios_base
are also available
to other stream classes. Before discussing these additional stream classes,
the features offered by the class ios
(and by implication: by
ios_base
) are now introduced.
In some cases it may be required to include ios
explicitly. An example
is the situations where the formatting flags themselves (cf. section
6.3.2.2) are referred to in source code.
The class ios
offers several member functions, most of which are related
to formatting. Other frequently used member functions are:
std::streambuf *ios::rdbuf()
:A pointer to thestreambuf
object forming the interface between theios
object and the device with which theios
object communicates is returned. See sections 14.8 and 24.1.2 for more information about theclass streambuf
.
std::streambuf *ios::rdbuf(std::streambuf *new)
:
The currentios
object is associated 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
.
std::ostream *ios::tie()
:A pointer to theostream
object that is currently tied to theios
object is returned (see the next member). The return value 0 indicates that currently noostream
object is tied to theios
object. See section 6.5.5 for details.
std::ostream *ios::tie(std::ostream *outs)
:
The ostream object is tied to currentios
object. This means that theostream
object is flushed every time before an input or output action is performed by the currentios
object. A pointer to theios
object's originalostream
object is returned. To break the tie, pass the argument 0. See section 6.5.5 for an example.
Conditions are represented by the following condition flags:
ios::badbit
:if this flag has been raised an illegal operation has been
requested at the level of the streambuf
object to which the stream
interfaces. See the member functions below for some examples.
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 performed by the
stream object has failed (like an attempt to extract an int
when no
numeric characters are available on input). In this case the stream itself
could not perform the operation that was requested of it.
ios::goodbit
:this flag is raised when none of the other three condition flags were raised.
Several condition member functions are available to manipulate or
determine the states of ios
objects. Originally they returned int
values, but their current return type is bool
:
bool ios::bad()
:
the valuetrue
is returned when the stream'sbadbit
has been set andfalse
otherwise. Iftrue
is returned it indicates that an illegal operation has been requested at the level of thestreambuf
object to which the stream interfaces. What does this mean? It indicates that thestreambuf
itself is behaving unexpectedly. Consider the following example:std::ostream error(0);Here anostream
object is constructed without providing it with a workingstreambuf
object. Since this `streambuf
' will never operate properly, itsbadbit
flag is raised from the very beginning:error.bad()
returnstrue
.
bool ios::eof()
:
the valuetrue
is returned when end of file (EOF
) has been sensed (i.e., theeofbit
flag has been set) andfalse
otherwise. Assume we're reading lines line-by-line fromcin
, but the last line is not terminated by a final\n
character. In that casestd::getline
attempting to read the\n
delimiter hits end-of-file first. This raises theeofbit
flag andcin.eof()
returnstrue
. For example, assumestd::string str
andmain
executing the statements:getline(cin, str); cout << cin.eof();Thenecho "hello world" | programprints the value 0 (no EOF sensed). But afterecho -n "hello world" | programthe value 1 (EOF sensed) is printed.
bool ios::fail()
:
the valuetrue
is returned whenbad
returnstrue
or when thefailbit
flag was set. The valuefalse
is returned otherwise. In the above example,cin.fail()
returnsfalse
, whether we terminate the final line with a delimiter or not (as we've read a line). However, executing anothergetline
results in raising thefailbit
flag, causingcin::fail()
to returntrue
. In general:fail
returnstrue
if the requested stream operation failed. A simple example showing this consists of an attempt to extract anint
when the input stream contains the texthello world
. The valuenot fail()
is returned by thebool
interpretation of a stream object (see below).
ios::good()
:
the value of thegoodbit
flag is returned. It equalstrue
when none of the other condition flags (badbit, eofbit, failbit
) was raised. Consider the following little program:#include <iostream> #include <string> using namespace std; void state() { cout << "\n" "Bad: " << cin.bad() << " " "Fail: " << cin.fail() << " " "Eof: " << cin.eof() << " " "Good: " << cin.good() << '\n'; } int main() { string line; int x; cin >> x; state(); cin.clear(); getline(cin, line); state(); getline(cin, line); state(); }When this program processes a file having two lines, containing, respectively,hello
andworld
, while the second line is not terminated by a\n
character the following is shown:Bad: 0 Fail: 1 Eof: 0 Good: 0 Bad: 0 Fail: 0 Eof: 0 Good: 1 Bad: 0 Fail: 0 Eof: 1 Good: 0Thus, extractingx
fails (good
returningfalse
). Then, the error state is cleared, and the first line is successfully read (good
returningtrue
). Finally the second line is read (incompletely):good
returningfalse
, andeof
returningtrue
.
bool
values:
streams may be used in expressions expecting logical values. Some examples are:if (cin) // cin itself interpreted as bool if (cin >> x) // cin interpreted as bool after an extraction if (getline(cin, str)) // getline returning cinWhen interpreting a stream as a logical value, it is actually `not fail()
' that is interpreted. The above examples may therefore be rewritten as:if (not cin.fail()) if (not (cin >> x).fail()) if (not getline(cin, str).fail())The former incantation, however, is used almost exclusively.
The following members are available to manage error states:
void ios::clear()
:
When an error condition has occurred, and the condition can be repaired, thenclear
can be used to clear the error state of the file. An overloaded version exists accepting state flags, that are set after first clearing the current set of flags:clear(int state)
. Its return type isvoid
ios::iostate ios::rdstate()
:
The current set of flags that are set for anios
object are returned (as anint
). To test for a particular flag, use the bitwise and operator:if (!(iosObject.rdstate() & ios::failbit)) { // last operation didn't fail }Note that this test cannot be performed for thegoodbit
flag as its value equals zero. To test for `good' use a construction like:if (iosObject.rdstate() == ios::goodbit) { // state is `good' }
void ios::setstate(ios::iostate state)
:
A stream may be assigned a certain set of states usingThe membersetstate
. Its return type isvoid
. E.g.,cin.setstate(ios::failbit); // set state to `fail'To set multiple flags in onesetstate()
call use thebitor
operator:cin.setstate(ios::failbit | ios::eofbit)
clear
is a shortcut to clear all error
flags. Of course, clearing the flags doesn't automatically mean the
error condition has been cleared too. The strategy should be:
clear
is called.
Formatting is used when it is necessary to, e.g., set the width of an output
field or input buffer and to determine the form (e.g., the radix) in
which values are displayed. Most formatting features belong to the realm of
the ios
class. Formatting is controlled by flags, defined by the ios
class. These flags may be manipulated in two ways: using specialized
member functions or using manipulators, which are directly inserted into
or extracted from streams. There is no special reason for using either
method; usually both methods are possible. In the following overview
the various member functions are first introduced. Following this the flags
and manipulators themselves are covered. Examples are provided showing how
the flags can be manipulated and what their effects are.
Many manipulators are parameterless and are available once a stream header
file (e.g., iostream
) has been included. Some manipulators require
arguments. To use the latter manipulators the header file iomanip
must be
included.
ios &ios::copyfmt(ios &obj)
:all format flags ofobj
are copied to the currentios
object. The currentios
object is returned.
ios::fill() const
:the current padding character is returned. By default, this is the blank space.
ios::fill(char padding)
:
the padding character is redefined, the previous padding character is returned. Instead of using this member function thesetfill
manipulator may be inserted directly into anostream
. Example:cout.fill('0'); // use '0' as padding char cout << setfill('+'); // use '+' as padding char
ios::fmtflags ios::flags() const
:the current set of flags controlling the format state of the stream for which the member function is called is returned. To inspect whether a particular flag was set, use thebit_and
operator. Example:if (cout.flags() & ios::hex) cout << "Integral values are printed as hex numbers\n"
ios::fmtflags ios::flags(ios::fmtflags flagset)
:
the previous set of flags are returned and the new set of flags are defined byflagset
. Multiple flags are specified using thebitor
operator. Example:// change the representation to hexadecimal cout.flags(ios::hex | cout.flags() & ~ios::dec);
int ios::precision() const
:the number of significant digits used when outputting floating point values is returned (default: 6).
int ios::precision(int signif)
:
the number of significant digits to use when outputting real values is set tosignif
. The previously used number of significant digits is returned. If the number of required digits exceedssignif
then the number is displayed in `scientific' notation (cf. section 6.3.2.2). Manipulator:setprecision
. Example:cout.precision(3); // 3 digits precision cout << setprecision(3); // same, using the manipulator cout << 1.23 << " " << 12.3 << " " << 123.12 << " " << 1234.3 << '\n'; // displays: 1.23 12.3 123 1.23e+03
ios::fmtflags ios::setf(ios::fmtflags flags)
:
sets one or more formatting flags (use thebitor
operator to combine multiple flags). Already set flags are not affected. The previous set of flags is returned. Instead of using this member function the manipulatorsetiosflags
may be used. Examples are provided in the next section (6.3.2.2).
ios::fmtflags ios::setf(ios::fmtflags flags, ios::fmtflags mask)
:
clears all flags mentioned inmask
and sets the flags specified inflags
. The previous set of flags is returned. Some examples are (but see the next section (6.3.2.2) for a more thorough discussion):// left-adjust information in wide fields cout.setf(ios::left, ios::adjustfield); // display integral values as hexadecimal numbers cout.setf(ios::hex, ios::basefield); // display floating point values in scientific notation cout.setf(ios::scientific, ios::floatfield);
ios::fmtflags ios::unsetf(fmtflags flags)
:
the specified formatting flags are cleared (leaving the remaining flags unaltered) and returns the previous set of flags. A request to unset an active default flag (e.g.,cout.unsetf(ios::dec)
) is ignored. Instead of this member function the manipulatorresetiosflags
may also be used. Example:cout << 12.24; // displays 12.24 cout << setf(ios::fixed); cout << 12.24; // displays 12.240000 cout.unsetf(ios::fixed); // undo a previous ios::fixed setting. cout << 12.24; // displays 12.24 cout << resetiosflags(ios::fixed); // using manipulator rather // than unsetf
int ios::width() const
:
the currently active output field width to use on the next insertion is returned. The default value is 0, meaning `as many characters as needed to write the value'.
int ios::width(int nchars)
:
the field width of the next insertion operation is set tonchars
, returning the previously used field width. This setting is not persistent. It is reset to 0 after every insertion operation. Manipulator:std::setw(int)
. Example:cout.width(5); cout << 12; // using 5 chars field width cout << setw(12) << "hello"; // using 12 chars field width
To display information in wide fields:
ios::internal
:
to add fill characters (blanks by default) between the minus sign of negative numbers and the value itself. Other values and data types are right-adjusted. Manipulator:std::internal
. Example:cout.setf(ios::internal, ios::adjustfield); cout << internal; // same, using the manipulator cout << '\'' << setw(5) << -5 << "'\n"; // displays '- 5'
ios::left
:to left-adjust values in fields that are wider than needed to display the values. Manipulator:std::left
. Example:cout.setf(ios::left, ios::adjustfield); cout << left; // same, using the manipulator cout << '\'' << setw(5) << "hi" << "'\n"; // displays 'hi '
ios::right
:to right-adjust values in fields that are wider than needed to display the values. Manipulator:std::right
. This is the default. Example:cout.setf(ios::right, ios::adjustfield); cout << right; // same, using the manipulator cout << '\'' << setw(5) << "hi" << "'\n"; // displays ' hi'
Using various number representations:
ios::dec
:to display integral values as decimal numbers. Manipulator:std::dec
. This is the default. Example:cout.setf(ios::dec, ios::basefield); cout << dec; // same, using the manipulator cout << 0x10; // displays 16
ios::hex
:to display integral values as hexadecimal numbers. Manipulator:std::hex
. Example:cout.setf(ios::hex, ios::basefield); cout << hex; // same, using the manipulator cout << 16; // displays 10
ios::oct
:to display integral values as octal numbers. Manipulator:std::oct
. Example:cout.setf(ios::oct, ios::basefield); cout << oct; // same, using the manipulator cout << 16; // displays 20
std::setbase(int radix)
:This is a manipulator that can be used to change the number representation to decimal, hexadecimal or octal. Example:cout << setbase(8); // octal numbers, use 10 for // decimal, 16 for hexadecimal cout << 16; // displays 20
Fine-tuning displaying values:
ios::boolalpha
:logical values may be displayed as text using the text `true
' for thetrue
logical value, and `false
' for thefalse
logical value usingboolalpha
. By default this flag is not set. Complementary flag:ios::noboolalpha
. Manipulators:std::boolalpha
andstd::noboolalpha
. Example:cout.setf(ios::boolalpha); cout << boolalpha; // same, using the manipulator cout << (1 == 1); // displays true
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. Complementary flag:ios::noshowbase
. Manipulators:std::showbase
andstd::noshowbase
. Example:cout.setf(ios::showbase); cout << showbase; // same, using the manipulator cout << hex << 16; // displays 0x10
ios::showpos
:to display the+
sign with positive decimal (only) values. Complementary flag:ios::noshowpos
. Manipulators:std::showpos
andstd::noshowpos
. Example:cout.setf(ios::showpos); cout << showpos; // same, using the manipulator cout << 16; // displays +16 cout.unsetf(ios::showpos); // Undo showpos cout << 16; // displays 16
ios::uppercase
:to display letters in hexadecimal values using capital letters. Complementary flag:ios::nouppercase
. Manipulators:std::uppercase
andstd::nouppercase
. By default lower case letters are used. Example:cout.setf(ios::uppercase); cout << uppercase; // same, using the manipulator cout << hex << showbase << 3735928559; // displays 0XDEADBEEF
Displaying floating point numbers
ios::fixed
:
to display real values using a fixed decimal point (e.g., 12.25 rather than 1.225e+01), thefixed
formatting flag is used. It can be used to set a fixed number of digits behind the decimal point. Manipulator:fixed
. Example:cout.setf(ios::fixed, ios::floatfield); cout.precision(3); // 3 digits behind the . // Alternatively: cout << setiosflags(ios::fixed) << setprecision(3); cout << 3.0 << " " << 3.01 << " " << 3.001 << '\n'; << 3.0004 << " " << 3.0005 << " " << 3.0006 << '\n' // Results in: // 3.000 3.010 3.001 // 3.000 3.001 3.001The example shows that 3.0005 is rounded away from zero, becoming 3.001 (likewise -3.0005 becomes -3.001). First setting precision and thenfixed
has the same effect.
ios::scientific
:to display real values in scientific notation (e.g., 1.24e+03). Manipulator:std::scientific
. Example:cout.setf(ios::scientific, ios::floatfield); cout << scientific; // same, using the manipulator cout << 12.25; // displays 1.22500e+01
ios::showpoint
:to display a trailing decimal point and trailing decimal zeros when real numbers are displayed. Complementary flag:ios::noshowpoint
. Manipulators:std::showpoint
,std::noshowpoint
. Example:cout << fixed << setprecision(3); // 3 digits behind . cout.setf(ios::showpoint); // set the flag cout << showpoint; // same, using the manipulator cout << 16.0 << ", " << 16.1 << ", " << 16; // displays: 16.000, 16.100, 16Note that the final 16 is an integral rather than a floating point number, so it has no decimal point. Soshowpoint
has no effect. Ifios::showpoint
is not active trailing zeros are discarded. If the fraction is zero the decimal point is discarded as well. Example:cout.unsetf(ios::fixed, ios::showpoint); // unset the flags cout << 16.0 << ", " << 16.1; // displays: 16, 16.1
Handling white space and flushing streams
std::endl
:manipulator inserting a newline character and flushing the stream. Often flushing the stream is not required and doing so would needlessly slow down I/O processing. Consequently, usingendl
should be avoided (in favor of inserting'\n'
) unless flusing the stream is explicitly intended. Note that streams are automatically flushed when the program terminates or when a stream is `tied' to another stream (cf.tie
in section 6.3). Example:cout << "hello" << endl; // prefer: << '\n';
std::ends
:manipulator inserting a 0-byte into a stream. It is usually used in combination with memory-streams (cf. section 6.4.3).
std::flush
:a stream may be flushed using this member. Often flushing the stream is not required and doing so would needlessly slow down I/O processing. Consequently, usingflush
should be avoided unless it is explicitly required to do so. Note that streams are automatically flushed when the program terminates or when a stream is `tied' to another stream (cf.tie
in section 6.3). Example:cout << "hello" << flush; // avoid if possible.
ios::skipws
:leading white space characters (blanks, tabs, newlines, etc.) are skipped when a value is extracted from a stream. This is the default. If the flag is not set, leading white space characters are not skipped. Manipulator:std::skipws
. Example:cin.setf(ios::skipws); // to unset, use // cin.unsetf(ios::skipws) cin >> skipws; // same, using the manipulator int value; cin >> value; // skips initial blanks
ios::unitbuf
:the stream for which this flag is set flushes its buffer after every output operation Often flushing a stream is not required and doing so would needlessly slow down I/O processing. Consequently, settingunitbuf
should be avoided unless flusing the stream is explicitly intended. Note that streams are automatically flushed when the program terminates or when a stream is `tied' to another stream (cf.tie
in section 6.3). Complementary flag:ios::nounitbuf
. Manipulators:std::unitbuf
,std::nounitbuf
. Example:cout.setf(ios::unitbuf); cout << unitbuf; // same, using the manipulator cout.write("xyz", 3); // flush follows write.
std::ws
:manipulator removing all white space characters (blanks, tabs, newlines, etc.) at the current file position. White space are removed if present even if the flagios::noskipws
has been set. Example (assume the input contains 4 blank characters followed by the characterX
):cin >> ws; // skip white space cin.get(); // returns 'X'
std::ostream
class. The ostream
class defines the basic operators and members inserting
information into streams: the insertion operator (<<), and special
members like write
writing unformatted information to streams.
The class ostream
acts as base class for several other classes, all
offering the functionality of the ostream
class, but adding their own
specialties. In the upcoming sections the following classes are discussed:
ostream
, offering the basic output facilities;
ofstream
, allowing us to write files
(comparable to C's fopen(filename, "w")
);
ostringstream
, allowing us to write information to
memory (comparable to C's sprintf
function).
ostream
defines basic output facilities. The cout
, clog
and cerr
objects are all ostream
objects. All facilities related to
output as defined by the ios
class are also available in the ostream
class.
We may define ostream
objects using the following
ostream constructor:
std::ostream object(std::streambuf *sb)
:
this constructor creates anostream
object which is a wrapper around an existingstd::streambuf
object. It isn't possible to define a plainostream
object (e.g., usingstd::ostream out;
) that can thereupon be used for insertions. Whencout
or its friends are used, we are actually using a predefinedostream
object that has already been defined for us and interfaces to the standard output stream using a (also predefined)streambuf
object handling the actual interfacing.It is, however, possible to define an
ostream
object passing it a 0-pointer. Such an object cannot be used for insertions (i.e., it raises itsios::bad
flag when something is inserted into it), but it may be given astreambuf
later. Thus it may be preliminary constructed, suspending its use until an appropriatestreambuf
becomes available (see also section 13.6).
ostream
class in C++ sources, the
<ostream>
header file must be included. To use the predefined
ostream
objects (std::cin, std::cout
etc.) the <iostream>
header
file must be included.
ostream
supports both formatted and binary output.
The insertion operator (<<) is used to insert values in
a type safe way into ostream
objects. This is called
formatted output, as binary values which are stored in the computer's
memory are converted to human-readable ASCII characters according to
certain formatting rules.
The insertion operator points to the ostream
object to
receive the information. The normal associativity of <<
remains unaltered, so when a statement like
cout << "hello " << "world";is encountered, the leftmost two operands are evaluated first (
cout
<< "hello "
), and an ostream &
object, which is actually the
same cout
object, is returned. Now, the statement is reduced to
cout << "world";and the second string is inserted into
cout
.
The << operator has a lot of (overloaded) variants, so many types of
variables can be inserted into ostream
objects. There is an overloaded
<<-operator expecting an int
, a double
, a pointer, etc. etc..
Each operator returns the ostream
object into which the information so far
has been inserted, and can thus immediately be followed by the next insertion.
Streams lack facilities for formatted output like C's
printf
and vprintf
functions. Although it is not difficult to
implement these facilities in the world of streams, printf
-like
functionality is hardly ever required in C++ programs. Furthermore, as it
is potentially type-unsafe, it might be better to avoid this functionality
completely.
When binary files must be written, normally no
text-formatting is used or required: an int
value should be written as a
series of raw bytes, not as a series of ASCII numeric characters 0 to
9. The following member functions of ostream
objects may be used to
write `binary files':
ostream& put(char c)
:to write 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& write(char const *buffer, int length)
:to write at mostThe bytes written by the abovelength
bytes, stored in thechar const *buffer
to theostream
object. Bytes are written as they are stored in the buffer, no formatting is done whatsoever. Note that the first argument is achar const *
: a type cast is required to write any other type. For example, to write anint
as an unformatted series of byte-values use:int x; out.write(reinterpret_cast<char const *>(&x), sizeof(int));
write
call are written to the
ostream
in an order depending on the endian-ness of the
underlying hardware. Big-endian computers write the most significant byte(s)
of multi-byte values first, little-endian computers first write the least
significant byte(s).
ostream
object supports repositioning, they usually
do. This means that it is possible to rewrite a section of the stream which
was written earlier. Repositioning is frequently used in database applications
where it must be possible to access the information in the database at random.
The current position can be obtained and modified using the following members:
ios::pos_type tellp()
:the current (absolute) position in the file where the next write-operation to the stream will take place is returned.
ostream &seekp(ios::off_type step, ios::seekdir org)
:modifies a stream's actual position. The function expects anoff_type
step
representing the number of bytes the current stream position is moved with respect toorg
. Thestep
value may be negative, zero or positive.The origin of the step,
org
is a value in theios::seekdir
enumeration. Its values are:It is OK to seek or write beyond the last file position. Writing bytes to a location beyond
ios::beg
:the stepsize is computed relative to the beginning of the stream. This value is used by default.ios::cur
:the stepsize is computed relative to the current position of the stream (as returned bytellp
).ios::end
:the stepsize is interpreted relative to the current end position of the stream.EOF
will pad the intermediate bytes with 0-valued bytes: null-bytes. Seeking beforeios::beg
raises theios::fail
flag.
ios::unitbuf
flag has been set, information written to an
ostream
object is not immediately written to the physical stream. Rather,
an internal buffer is filled during the write-operations, and when full it
is flushed.
The stream's internal buffer can be flushed under program control:
ostream& flush()
:any buffered information stored internally by theostream
object is flushed to the device to which theostream
object interfaces. A stream is flushed automatically when:
std::ofstream
class is derived from the ostream
class:
it has the same capabilities as the ostream
class, but can be used to
access files or create files for writing.
In order to use the ofstream
class in C++ sources, the
<fstream>
header file must be included. Including fstream
does not
automatically make available the standard streams cin
, cout
and
cerr
. Include iostream
to declare these standard streams.
The following constructors are available for
ofstream
objects:
ofstream object
:
this is the basic constructor. It defines anofstream
object which may be associated with an actual file later, using itsopen()
member (see below).
ofstream object(char const *name, ios::openmode mode = ios::out)
:
this constructor defines anofstream
object and associates it immediately with the file namedname
using output modemode
. Section 6.4.2.1 provides an overview of available output modes. Example: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
std::streambuf
object (and in some implementations: with a
std::filebuf
object, which is also a streambuf
). Streambuf
objects
are discussed in section 14.8, filebuf
objects are discussed in
section 14.8.2.
Instead of directly associating an ofstream
object with a file, the
object can be constructed first, and opened later.
void open(char const *name,
ios::openmode mode = ios::out)
:associates anofstream
object with an actual file. If theios::fail
flag was set before callingopen
and opening succeeds the flag is cleared. Opening an already open stream fails. To reassociate a stream with another file it must first be closed:ofstream out("/tmp/out"); out << "hello\n"; out.close(); // flushes and closes out out.open("/tmp/out2"); out << "world\n";
void close()
:closes theofstream
object. The function sets theios::fail
flag of the closed object. Closing the file flushes any buffered information to the associated file. A file is automatically closed when the associatedofstream
object ceases to exist.
bool is_open() const
:assume a stream was properly constructed, but it has not yet been attached to a file. E.g., the statementofstream ostr
was executed. When we now check its status throughgood()
, a non-zero (i.e., OK) value is returned. The `good' status here indicates that the stream object has been constructed properly. It doesn't mean the file is also open. To test whether a stream is actually open,is_open
should be called. If it returnstrue
, the stream is open. Example:#include <fstream> #include <iostream> using namespace std; int main() { ofstream of; cout << "of's open state: " << boolalpha << of.is_open() << '\n'; of.open("/dev/null"); // on Unix systems cout << "of's open state: " << of.is_open() << '\n'; } /* Generated output: of's open state: false of's open state: true */
ofstream
(or istream
, see section 6.5.2)
objects. The values are of type ios::openmode
. Flags may be
combined using the bitor
operator.
ios::app
:reposition the stream to its end before every output command (see
also ios::ate
below). The
file is created if it doesn't yet exist. When opening a stream in this mode
any existing contents of the file are kept.
ios::ate
:start initially at the end of the file. Note that any existing contents are only kept if some other flag tells the object to do so. For exampleofstream out("gone", ios::ate)
rewrites the filegone
, because the impliedios::out
causes the rewriting. If rewriting of an existing file should be prevented, theios::in
mode should be specified too. However, whenios::in
is specified the file must already exist. Theate
mode only initially positions the file at the end of file position. After that information may be written in the middle of the file usingseekp
. When theapp
mode is used information is only written at end of file (effectively ignoringseekp
operations).
ios::binary
:open a file in binary mode (used on systems distinguishing text- and binary files, like MS-Windows).
ios::in
:open the file for reading. The file must exist.
ios::out
:open the file for writing. 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.
in | out: The stream may be read and written. However, the file must exist. in | out | trunc: The stream may be read and written. It is (re)created empty first.An interesting subtlety is that the
open
members of the ifstream,
ofstream
and fstream
classes have a second parameter of type
ios::openmode
. In contrast to this, the bitor
operator returns an
int
when applied to two enum-values. The question why the bitor
operator may nevertheless be used here is answered in a later chapter
(cf. section 11.11).
stream
facilities, std::ostringstream
objects should be used.
As the class ostringstream
is derived from the class ostream
all
ostream
's facilities are available to ostringstream
objects as
well. To use and define ostringstream
objects the header file
<sstream>
must be included. In addition the class ostringstream
offers
the following constructors and members:
ostringstream ostr(string const &init,
ios::openmode mode = ios::out)
:when specifyingopenmode
asios::ate
, theostringstream
object is initialized by thestring init
and remaining insertions are appended to the contents of theostringstream
object.
ostringstream ostr(ios::openmode mode = ios::out)
:
this constructor can also be used as default constructor. Alternatively it allows, e.g., forced additions at the end of the information stored in the object so far (usingios::app
). Example:std::ostringstream out;
std::string str() const
:a copy of the string that is stored inside the ostringstream
object is returned.
void str(std::string const &str)
:
the current object is reinitialized with new initial contents.
ostringstream
class:
several values are inserted into the object. Then, the text contained by the
ostringstream
object is stored in a std::string
, whose length and
contents are thereupon printed. Such ostringstream
objects are most often
used for doing `type to string' conversions,
like converting int
values to text. Formatting flags can be used with
ostringstreams
as well, as they are part of the ostream
class.
Here is an example showing an ostringstream
object being used:
#include <iostream> #include <sstream> using namespace std; int main() { ostringstream ostr("hello ", ios::ate); cout << ostr.str() << '\n'; ostr.setf(ios::showbase); ostr.setf(ios::hex, ios::basefield); ostr << 12345; cout << ostr.str() << '\n'; ostr << " -- "; ostr.unsetf(ios::hex); ostr << 12; cout << ostr.str() << '\n'; ostr.str("new text"); cout << ostr.str() << '\n'; ostr.seekp(4, ios::beg); ostr << "world"; cout << ostr.str() << '\n'; } /* Output from this program: hello hello 0x3039 hello 0x3039 -- 12 new text new world */
std::istream
class. The istream
class defines the basic operators and members
extracting information from streams: the extraction operator (>>),
and special members like istream::read
reading unformatted
information from streams.
The class istream
acts as base class for several other classes, all
offering the functionality of the istream
class, but adding their own
specialties. In the upcoming sections the following classes are discussed:
istream
, offering the basic facilities for doing input;
ifstream
, allowing us to read files
(comparable to C's fopen(filename, "r")
);
istringstream
, allowing us to read information from
text that is not stored on files (streams) but in memory (comparable to
C's sscanf
function).
istream
defines basic input facilities. The cin
object, is
an istream
object. All facilities related to input as defined by the
ios
class are also available in the istream
class.
We may define istream
objects using the following
istream constructor:
istream object(streambuf *sb)
:
this constructor can be used to construct a wrapper around an existingstd::streambuf
object. Similarly toostream
objects,istream
objects may be defined by passing it initially a 0-pointer. See section 6.4.1 for a discussion, see also section 13.6, and see chapter 24 for examples.
istream
class in C++ sources, the
<istream>
header file must be included. To use the predefined
istream
object cin
, the <iostream>
header file must be included.
istream
supports both formatted and unformatted
binary input. The extraction operator (operator>>
) is
used to extract values in a type safe way from istream
objects. This
is called formatted input, whereby human-readable ASCII characters are
converted, according to certain formatting rules, to binary values.
The extraction operator points to the objects or variables which receive new values. The normal associativity of >> remains unaltered, so when a statement like
cin >> x >> y;is encountered, the leftmost two operands are evaluated first (
cin
>> x
), and an istream &
object, which is actually the same cin
object, is returned. Now, the
statement is reduced to
cin >> yand the
y
variable is extracted from cin
.
The >> operator has many (overloaded) variants and thus many types of
variables can be extracted from istream
objects. There is an overloaded
>> available for the extraction of an int
, of a double
, of a
string, of an array of characters, possibly to a pointer, etc. etc.. String or
character array extraction
by default first skips all white space characters, and then extracts
all consecutive non-white space characters. Once an extraction operator has
been processed the istream
object from which the information was extracted
is returned and it can immediately be used for additional istream
operations that appear in the same expression.
Streams lack facilities for formatted input (as used by, e.g., C's
scanf
and vscanf
functions). Although it is not difficult to add these
facilities to the world of streams, scanf
-like functionality is hardly
ever required in C++ programs. Furthermore, as it is potentially
type-unsafe, it might be better to avoid formatted input completely.
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 gcount() const
:the number of characters read from the input stream by the last unformatted input operation is returned.
int get()
:the next available single character is returned as an unsignedchar
value using anint
return type.EOF
is returned if no more character are available.
istream &get(char &ch)
:the next single character read from the input stream is
stored in ch
. The member function returns the stream itself which may be
inspected to determine whether a character was obtained or not.
istream& get(char *buffer, int len, char delim = '\n')
:At mostlen - 1
characters are read from the input stream into the array starting atbuffer
, which should be at leastlen
bytes long. Reading also stops when the delimiterdelim
is encountered. However, the delimiter itself is not removed from the input stream.Having stored the characters into
buffer
, an 0-valued character is written beyond the last character stored into thebuffer
. The functionseof
andfail
(see section 6.3.1) return 0 (false
) if the delimiter was encountered before readinglen - 1
characters or if the delimiter was not encountered after readinglen - 1
characters. It is OK to specifiy an 0-valued character delimiter: this way NTB strings may be read from a (binary) file.
istream& getline(char *buffer, int len, char delim = '\n')
:this member function operates analogously to theget
member function, butgetline
removesdelim
from the stream if it is actually encountered. The delimiter itself, if encountered, is not stored in thebuffer
. Ifdelim
was not found (before readinglen - 1
characters) thefail
member function, and possibly alsoeof
returns true. Realize that thestd::string
class also offers a functionstd::getline
which is generally preferred over thisgetline
member function that is described here (see section 5.2.4).
istream& ignore()
:one character is skipped from the input stream.
istream& ignore(int n)
:n
characters are skipped from the input stream.
istream& ignore(int n, int delim)
:at mostn
characters are skipped but skipping characters stops after having removeddelim
from the input stream.
int peek()
:this function returns the next available input character,
but does not actually remove the character from the input stream. EOF
is returned if no more characters are available.
istream& putback(char ch)
:The characterch
is `pushed back' into the input stream, to be read again as the next available character.EOF
is returned if this is not allowed. Normally, it is OK to put back one character. Example:string value; cin >> value; cin.putback('X'); // displays: X cout << static_cast<char>(cin.get());
istream &read(char *buffer, int len)
:At mostlen
bytes are read from the input stream into the buffer. IfEOF
is encountered first, fewer bytes are read, with the member functioneof
returningtrue
. This function is commonly used when reading binary files. Section 6.5.2 contains an example in which this member function is used. The member functiongcount()
may be used to determine the number of characters that were retrieved byread
.
istream& readsome(char *buffer, int len)
:at mostlen
bytes are read from the input stream into the buffer. All available characters are read into the buffer, but ifEOF
is encountered, fewer bytes are read, without setting theios::eofbit
orios::failbit
.
istream &unget()
:the last character that was read from the stream is put back.
istream
object supports repositioning, some do. This
means that it is possible to read the same section of a stream
repeatedly. Repositioning is frequently used in database applications
where it must be possible to access the information in the database randomly.
The current position can be obtained and modified using the following members:
ios::pos_type tellg()
:the stream's current (absolute) position where the stream's next read-operation will take place is returned.
istream &seekg(ios::off_type step, ios::seekdir org)
:modifies a stream's actual position. The function expects anoff_type
step
representing the number of bytes the current stream position is moved with respect toorg
. Thestep
value may be negative, zero or positive.The origin of the step,
org
is a value in theios::seekdir
enumeration. Its values are:It is OK to seek beyond the last file position. Seeking before
ios::beg
:the stepsize is computed relative to the beginning of the stream. This value is used by default.ios::cur
:the stepsize is computed relative to the current position of the stream (as returned bytellp
).ios::end
:the stepsize is interpreted relative to the current end position of the the stream.ios::beg
raises theios::failbit
flag.
std::ifstream
class is derived from the istream
class:
it has the same capabilities as the istream
class, but can be used to
access files for reading.
In order to use the ifstream
class in C++ sources, the
<fstream>
header file must be included. Including fstream
does not
automatically make available the standard streams cin
, cout
and
cerr
. Include iostream
to declare these standard streams.
The following constructors are available for
ifstream
objects:
ifstream object
:
this is the basic constructor. It defines anifstream
object which may be associated with an actual file later, using itsopen()
member (see below).
ifstream object(char const *name, ios::openmode mode = ios::in)
:
this constructor can be used to define anifstream
object and associate it immediately with the file namedname
using input modemode
. Section 6.4.2.1 provides an overview of available input modes. Example:ifstream in("/tmp/input");
Instead of directly associating an ifstream
object with a file, the
object can be constructed first, and opened later.
void open(char const *name,
ios::openmode mode = ios::in)
:associates anifstream
object with an actual file. If theios::fail
flag was set before callingopen
and opening succeeds the flag is cleared. Opening an already open stream fails. To reassociate a stream with another file it must first be closed:ifstream in("/tmp/in"); in >> variable; in.close(); // closes in in.open("/tmp/in2"); in >> anotherVariable;
void close()
:closes theifstream
object. The function sets theios::fail
flag of the closed object. Closing the file flushes any buffered information to the associated file. A file is automatically closed when the associatedifstream
object ceases to exist.
bool is_open() const
:assume a stream was properly constructed, but it has not yet been attached to a file. E.g., the statementifstream ostr
was executed. When we now check its status throughgood()
, a non-zero (i.e., OK) value is returned. The `good' status here indicates that the stream object has been constructed properly. It doesn't mean the file is also open. To test whether a stream is actually open,is_open
should be called. If it returnstrue
, the stream is open. Also see the example in section 6.4.2. The following example illustrates reading from a binary file (see also section 6.5.1.1):#include <fstream> using namespace std; int main(int argc, char **argv) { ifstream in(argv[1]); double value; // reads double in raw, binary form from file. in.read(reinterpret_cast<char *>(&value), sizeof(double)); }
stream
facilities, std::istringstream
objects should
be used. As the class istringstream
is derived from the class istream
all istream
's facilities are available to istringstream
objects as
well. To use and define istringstream
objects the header file
<sstream>
must be included. In addition the class istringstream
offers
the following constructors and members:
istringstream istr(string const &init,
ios::openmode mode = ios::in)
:the object is initialized with init
's contents
istringstream istr(ios::openmode mode = ios::in)
(this constructor is usually used as the default
constructor. Example:
std::istringstream in;)
void str(std::string const &str)
:the current object is reinitialized with new initial contents.
istringstream
class:
several values are extracted from the object. Such istringstream
objects
are most often used for doing `string to type'
conversions, like converting text to int
values (cf. C's atoi
function). Formatting flags can be used with istringstreams
as well, as
they are part of the istream
class. In the example note especially the
use of the member seekg
:
#include <iostream> #include <sstream> using namespace std; int main() { istringstream istr("123 345"); // store some text. int x; istr.seekg(2); // skip "12" istr >> x; // extract int cout << x << '\n'; // write it out istr.seekg(0); // retry from the beginning istr >> x; // extract int cout << x << '\n'; // write it out istr.str("666"); // store another text istr >> x; // extract it cout << x << '\n'; // write it out } /* output of this program: 3 123 666 */
fail
returns
true
), break
from the loop
getline(istream &, string &)
(see section 6.5.1.1) returns an
istream &
, so here reading and testing may be contracted using one
expression. Nevertheless, the above mold represents the general case. So,
the following program may be used to copy cin
to cout
:
#include <iostream> using namespace::std; int main() { while (true) { char c; cin.get(c); if (cin.fail()) break; cout << c; } }
Contraction is possible here by combining get
with the
if
-statement, resulting in:
if (!cin.get(c)) break;Even so, this would still follow the basic rule: `read first, test later'.
Simply copying a file isn't required very often. More often a situation is
encountered where a file is processed up to a certain point, followed by plain
copying the file's remaining information. The next program illustrates
this. Using ignore
to skip the first line (for the sake of the example it
is assumed that the first line is at most 80 characters long), the second
statement uses yet another overloaded version of the <<-operator, in
which a streambuf
pointer is inserted into
a stream. As the member rdbuf
returns a stream's streambuf *
, we have
a simple means of inserting a stream's contents into an ostream
:
#include <iostream> using namespace std; int main() { cin.ignore(80, '\n'); // skip the first line and... cout << cin.rdbuf(); // copy the rest through the streambuf * }
This way of copying streams only assumes the existence of a streambuf
object. Consequently it can be used with all specializations of the
streambuf
class.
ios
objects
using the tie
member function. Tying results in flushing the ostream
's
buffer whenever an input or output operation is performed on the ios
object to which the ostream
object is tied. By default cout
is tied
to cin
(using cin.tie(cout)
). This tie means that whenever an
operation on cin
is requested, cout
is flushed first. To break the
tie, ios::tie(0)
can be called. In the example: cin.tie(0)
.
Another useful coupling of streams is shown by the tie between cerr
and
cout
. Because of the tie standard output and error messages written to the
screen are shown in sync with the time at which they were generated:
#include <iostream> using namespace std; int main() { cerr.tie(0); // untie cout << "first (buffered) line to cout "; cerr << "first (unbuffered) line to cerr\n"; cout << "\n"; cerr.tie(&cout); // tie cout to cerr cout << "second (buffered) line to cout "; cerr << "second (unbuffered) line to cerr\n"; cout << "\n"; } /* Generated output: first (unbuffered) line to cerr first (buffered) line to cout second (buffered) line to cout second (unbuffered) line to cerr */
An alternative way to couple streams is to make streams use a common
streambuf
object. This can be implemented using the
ios::rdbuf(streambuf *)
member function. This way two streams can use,
e.g. their own formatting, one stream can be used for input, the other for
output, and redirection using the stream library rather than operating
system calls can be implemented. See the next sections for examples.
ofstream out(string const &name) { ofstream ret(name); // construct ofstream return ret; // return value optimization, but } // OK as moving is supported int main() { ofstream mine(out("out")); // return value optimizations, but // OK as moving is supported ofstream base("base"); ofstream other; base.swap(other); // swapping streams is OK other = std::move(base); // moving streams is OK // other = base; // this would ail: copy assignment // is not available for streams }
ios::rdbuf
streams can be forced to share their
streambuf
objects. Thus information written to one
stream is actually written to another stream; a phenomenon normally
called redirection. Redirection is commonly implemented at the
operating system level, and sometimes that is still necessary (see
section 24.2.3).
A common situation where redirection is useful is when error messages should
be written to file rather than to the standard error stream, usually indicated
by its file descriptor number 2. In the Unix operating system using the
bash
shell, this can be realized as follows:
program 2>/tmp/error.logFollowing this command any error messages written by
program
are
saved on the file /tmp/error.log
, instead of appearing on the screen.
Here is an example showing how this can be implemented using streambuf
objects. Assume program
expects an argument defining the name
of the file to write the error messages to. It could be called as follows:
program /tmp/error.logThe program looks like this, an explanation is provided below the program's source text:
#include <iostream> #include <fstream> using namespace std; int main(int argc, char **argv) { ofstream errlog; // 1 streambuf *cerr_buffer = 0; // 2 if (argc == 2) { errlog.open(argv[1]); // 3 cerr_buffer = cerr.rdbuf(errlog.rdbuf()); // 4 } else { cerr << "Missing log filename\n"; return 1; } cerr << "Several messages to stderr, msg 1\n"; cerr << "Several messages to stderr, msg 2\n"; cout << "Now inspect the contents of " << argv[1] << "... [Enter] "; cin.get(); // 5 cerr << "Several messages to stderr, msg 3\n"; cerr.rdbuf(cerr_buffer); // 6 cerr << "Done\n"; // 7 } /* Generated output on file argv[1] at cin.get(): Several messages to stderr, msg 1 Several messages to stderr, msg 2 at the end of the program: Several messages to stderr, msg 1 Several messages to stderr, msg 2 Several messages to stderr, msg 3 */
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.
cerr
now writes 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 again written to the screen, as
the redirection has been terminated.
std::fstream
object must be
created. As with ifstream
and ofstream
objects, its constructor
receives the name of the file to be opened:
fstream inout("iofile", ios::in | ios::out);Note the use of the constants
ios::in
and ios::out
,
indicating that the file must be opened for both reading and writing. Multiple
mode indicators may be used, concatenated by the bitor
operator.
Alternatively, instead of ios::out
, ios::app
could have been
used and mere writing would become appending (at the end of the file).
Reading and writing to the same file is always a bit awkward: what to do when the file may not yet exist, but if it already exists it should not be rewritten? Having fought with this problem for some time I now use the following approach:
#include <fstream> #include <iostream> #include <string> using namespace std; int main() { fstream rw("fname", ios::out | ios::in); if (!rw) // file didn't exist yet { rw.clear(); // try again, creating it using ios::trunc rw.open("fname", ios::out | ios::trunc | ios::in); } if (!rw) // can't even create it: bail out { cerr << "Opening `fname' failed miserably" << '\n'; return 1; } cerr << "We're at: " << rw.tellp() << '\n'; // write something rw << "Hello world" << '\n'; rw.seekg(0); // go back and read what's written string s; getline(rw, s); cout << "Read: " << s << '\n'; }
Under this approach if the first construction attempt fails fname
doesn't exist yet. But then open
can be attempted using the
ios::trunc
flag. If the file already existed, the construction
would have succeeded. By specifying ios::ate
when defining rw
,
the initial read/write action would by default have taken place at
EOF
.
Under DOS-like operating systems that use the
multiple character sequence \r\n
to separate lines in text files the
flag ios::binary
is required to process binary files ensuring
that \r\n
combinations are processed as two characters. In general,
ios::binary
should be specified when binary (non-text) files are to be
processed. By default files are opened as text files. Unix operating
systems do not distinguish text files from binary files.
With fstream
objects, combinations of file flags are used to make sure
that a stream is or is not (re)created empty when opened. See section
6.4.2.1 for details.
Once a file has been opened in read and write mode, the << operator
can be used to insert information into the file, while the >> operator
may be used to extract information from the file. These operations may be
performed in any order, but a seekg
or seekp
operation is required
when switching between insertions and extractions. The seek operation is used
to activate the stream's data used for reading or those used for writing (and
vice versa). The istream
and ostream
parts of fstream
objects
share the stream's data buffer and by performing the seek operation the stream
either activates its istream
or its ostream
part. If the seek is
omitted, reading after writing and writing after reading simply fails. The
example shows a white space delimited word being read from a file, writing
another string to the file, just beyond the point where the just read word
terminated. Finally yet another string is read which is found just beyond the
location where the just written strings ended:
fstream f("filename", ios::in | ios::out); string str; f >> str; // read the first word // write a well known text f.seekg(0, ios::cur); f << "hello world"; f.seekp(0, ios::cur); f >> str; // and read againSince a seek or clear operation is required when alternating between read and write (extraction and insertion) operations on the same file it is not possible to execute a series of << and >> operations in one expression statement.
Of course, random insertions and extractions are hardly ever used. Generally,
insertions and extractions occur at well-known locations in a file. In those
cases, the position where insertions or extractions are required can be
controlled and monitored by the seekg
, seekp, tellg
and tellp
members (see sections 6.4.1.2 and 6.5.1.2).
Error conditions (see section 6.3.1) occurring due to, e.g., reading
beyond end of file, reaching end of file, or positioning before begin of file,
can be cleared by the clear
member function. Following clear
processing may continue. E.g.,
fstream f("filename", ios::in | ios::out); string str; f.seekg(-10); // this fails, but... f.clear(); // processing f continues f >> str; // read the first wordA situation where files are both read and written is seen in database applications, using files consisting of records having fixed sizes, and where locations and sizes of pieces of information are known. For example, the following program adds text lines to a (possibly existing) file. It can also be used to retrieve a particular line, given its order-number in the file. A binary file
index
allows for the quick
retrieval of the location of lines.
#include <iostream> #include <fstream> #include <string> #include <climits> using namespace std; void err(char const *msg) { cout << msg << '\n'; } void err(char const *msg, long value) { cout << msg << value << '\n'; } void read(fstream &index, fstream &strings) { int idx; if (!(cin >> idx)) // read index { cin.clear(); // allow reading again cin.ignore(INT_MAX, '\n'); // skip the line return err("line number expected"); } index.seekg(idx * sizeof(long)); // go to index-offset long offset; if ( !index.read // read the line-offset ( reinterpret_cast<char *>(&offset), sizeof(long) ) ) return err("no offset for line", idx); if (!strings.seekg(offset)) // go to the line's offset return err("can't get string offset ", offset); string line; if (!getline(strings, line)) // read the line return err("no line at ", offset); cout << "Got line: " << line << '\n'; // show the line } void write(fstream &index, fstream &strings) { string line; if (!getline(cin, line)) // read the line return err("line missing"); strings.seekp(0, ios::end); // to strings index.seekp(0, ios::end); // to index long offset = strings.tellp(); if ( !index.write // write the offset to index ( reinterpret_cast<char *>(&offset), sizeof(long) ) ) return err("Writing failed to index: ", offset); if (!(strings << line << '\n')) // write the line itself return err("Writing to `strings' failed"); // confirm writing the line cout << "Write at offset " << offset << " line: " << line << '\n'; } int main() { fstream index("index", ios::trunc | ios::in | ios::out); fstream strings("strings", ios::trunc | ios::in | ios::out); cout << "enter `r <number>' to read line <number> or " "w <line>' to write a line\n" "or enter `q' to quit.\n"; while (true) { cout << "r <nr>, w <line>, q ? "; // show prompt index.clear(); strings.clear(); string cmd; cin >> cmd; // read cmd if (cmd == "q") // process the cmd. return 0; if (cmd == "r") read(index, strings); else if (cmd == "w") write(index, strings); else if (cin.eof()) { cout << "\n" "Unexpected end-of-file\n"; return 1; } else cout << "Unknown command: " << cmd << '\n'; } }
Another example showing reading and writing of files is provided by the next program. It also illustrates the processing of NTB strings:
#include <iostream> #include <fstream> using namespace std; int main() { // r/w the file fstream f("hello", ios::in | ios::out | ios::trunc); f.write("hello", 6); // write 2 NTB strings f.write("hello", 6); f.seekg(0, ios::beg); // reset to begin of file char buffer[100]; // or: char *buffer = new char[100] char c; // read the first `hello' cout << f.get(buffer, sizeof(buffer), 0).tellg() << '\n'; f >> c; // read the NTB delim // and read the second `hello' cout << f.get(buffer + 6, sizeof(buffer) - 6, 0).tellg() << '\n'; buffer[5] = ' '; // change asciiz to ' ' cout << buffer << '\n'; // show 2 times `hello' } /* Generated output: 5 11 hello hello */
A completely different way to read and write streams may be implemented
using streambuf
members. All considerations mentioned so far remain valid
(e.g., before a read operation following a write operation seekg
must be
used). When streambuf
objects are used, either an
istream
is associated with the streambuf
object of another ostream
object, or an ostream
object is associated with the
streambuf
object of another istream
object. Here is the previous
program again, now using
associated streams:
#include <iostream> #include <fstream> #include <string> using namespace std; void err(char const *msg); // see earlier example void err(char const *msg, long value); void read(istream &index, istream &strings) { index.clear(); strings.clear(); // insert the body of the read() function of the earlier example } void write(ostream &index, ostream &strings) { index.clear(); strings.clear(); // insert the body of the write() function of the earlier example } int main() { ifstream index_in("index", ios::trunc | ios::in | ios::out); ifstream strings_in("strings", ios::trunc | ios::in | ios::out); ostream index_out(index_in.rdbuf()); ostream strings_out(strings_in.rdbuf()); cout << "enter `r <number>' to read line <number> or " "w <line>' to write a line\n" "or enter `q' to quit.\n"; while (true) { cout << "r <nr>, w <line>, q ? "; // show prompt string cmd; cin >> cmd; // read cmd if (cmd == "q") // process the cmd. return 0; if (cmd == "r") read(index_in, strings_in); else if (cmd == "w") write(index_out, strings_out); else cout << "Unknown command: " << cmd << '\n'; } }
In this example
streambuf
objects of existing
streams are not ifstream
or ofstream
objects but basic istream
and
ostream
objects.
streambuf
object is not defined by an ifstream
or
ofstream
object. Instead it is defined outside of the streams, using
a filebuf
(cf. section 14.8.2) and 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 with ofstream
objects. Conversely, an ofstream
objects can be
constructed using stream modes normally used with ifstream
objects.
istream
and ostreams
share a streambuf
, then their
read and write pointers (should) point to the shared buffer: 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 specifically be constructed to control and interface
particular devices. Elaborating this (see also section 14.8) is left
as an exercise to the reader.