FILE) approach, well known from the
C programming language, C++ offers an input/output (I/O)
library based on class concepts.
All C++ I/O facilities are defined in the namespace std. The std::
prefix is omitted below, except for situations where this would result in
ambiguities.
Earlier (in chapter 3) we've seen several examples of the use of the C++ I/O library, in particular showing insertion operator (<<) and the extraction operator (>>). In this chapter we'll cover I/O in more detail.
The discussion of input and output facilities provided by the C++
programming language heavily uses the class concept and the notion of
member functions. Although class construction has not yet been covered (for
that see chapter 7) and although inheritance is not covered
formally before chapter 13, it is quite possible to discuss I/O
facilities long before the technical background of class construction has been
covered.
Most C++ I/O classes have names starting with basic_ (like
basic_ios). However, these basic_ names are not regularly found in
C++ programs, as most classes are also defined through using
declarations like:
using ios = basic_ios<char>;
Since C++ supports various kinds of character types (e.g., char,
wchar_t), I/O facilities were developed using the template mechanism
allowing for easy conversions to character types other than the traditional
char type. As elaborated in chapter 21, this also allows the
construction of generic software, that could thereupon be used for any
particular type representing characters. So, analogously to the above
using declaration there exists a
using wios = basic_ios<wchar_t>;
This way, wios can be used for the wchar_t type. Because of the
existence of these type definitions, the basic_ prefix was omitted from
the C++ Annotations without loss of continuity. The C++ Annotations primarily
focus on the standard 8-bits char type.
Iostream objects cannot be declared using standard forward declarations, like:
class std::ostream; // now erroneous
Instead, to declare iostream classes the <iosfwd> header file
should be included:
#include <iosfwd> // correct way to declare iostream classes
Using C++ I/O offers the additional advantage of
type safety. Objects (or plain values) are inserted into
streams. Compare this to the situation commonly encountered in C where the
fprintf function is used to indicate by a format string what kind of
value to expect where. Compared to this latter situation C++'s
iostream approach immediately uses the objects where their values should
appear, as in
cout << "There were " << nMaidens << " virgins present\n";
The compiler notices the type of the nMaidens variable, inserting
its proper value at the appropriate place in the sentence inserted into
the cout iostream.
Compare this to the situation encountered in C. Although C compilers
are getting smarter and smarter, and although a well-designed
C compiler may warn you for a mismatch between a format specifier and the
type of a variable encountered in the corresponding position of the argument
list of a printf statement, it can't do much more than warn you.
The type safety seen in C++ prevents you from making type
mismatches, as there are no types to match.
Apart from this, iostreams offer more or less the same set of
possibilities as the standard FILE-based I/O used in C: files can be
opened, closed, positioned, read, written, etc.. In C++ the basic FILE
structure, as used in C, is still available. But C++ adds to this I/O
based on classes, resulting in type safety, extensibility, and a clean design.
The ANSI/ISO standard specifies architecture independent I/O. Not all of the standard's specifications are covered in this chapter, as they often rely on inheritance and polymorphism, which topics are formally covered by chapters 13 and 14. Some examples are offered in chapter 26, and in this chapter references to specific sections in other chapters are given where appropriate.
This chapter is organized as follows (see also Figure 4):ios_base is the foundation upon which the
iostream 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 facilities for output formatting.
ios is directly derived from
ios_base. Every class of the I/O library doing input or output is itself
derived from this ios class, and therefore 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.ios is important because it implements communication with a
buffer which is used by streams. This buffer is a streambuf object
which is responsible for the actual I/O to/from the actually used device,
which might be a file, a keyboard, a screen, an Internet connection, etc.,
etc.. Consequently iostream objects do not perform I/O operations
themselves, but leave these operations 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.
iostream combines the facilities offered by
istream and ostream. Thus, iostream objects can be used to read
and write from the same object.
fstream is a frequently encountered example of an
iostream class: fstream objects are used to read and write from the
same file, which is often used in programs processing data bases. In this
chapter topics like reading and writing from the same stream and
mixing C and C++ I/O using filebuf objects are also
covered. Other I/O related topics are covered elsewhere in the C++ Annotations
(cf. section 20.15 and chapter 26).
Stream objects have a limited but important role: they are the interface
between, on the one hand, the objects to be input or output and, on the other
hand, the streambuf, which is responsible for the actual input and output
to the device accessed by a streambuf object.
This approach allows us to construct a new kind of streambuf for a new
kind of device, and use that streambuf in combination with the `good old'
istream- and ostream-class facilities. It is important to understand
the distinction between the formatting roles of iostream objects and the
buffering interface to an external device as implemented in a streambuf
object. Interfacing to new devices (like sockets or
file descriptors) requires the construction of a new
kind of streambuf, rather than a new kind of istream or ostream
object. A wrapper class may be constructed around the istream or
ostream classes, though, to ease the access to a special device. This is
how the stringstream classes were constructed.
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.3.
<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) and also when defining a
stream capable of reading and writing from the same device (see also
section 14.8.2).
<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 4). Since all
stream classes in turn inherit from ios, and thus also from ios_base,
the distinction between ios_base and ios is in practice not
important. Therefore, facilities actually provided by ios_base will be
discussed as facilities provided by ios. The reader who is interested in
the true class in which a particular facility is defined should consult the
relevant header files (e.g., ios_base.h and basic_ios.h).
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():streambuf object forming the interface between
the ios object and the device with which the ios object communicates
is returned. See sections 14.8 and 26.1.2 for more information
about the class streambuf.
std::streambuf *ios::rdbuf(std::streambuf *new):
The currentiosobject is associated with anotherstreambufobject. A pointer to theiosobject's originalstreambufobject is returned. The object to which this pointer points is not destroyed when thestreamobject goes out of scope, but is owned by the caller ofrdbuf.
std::ostream *ios::tie():ostream object that is currently tied to the
ios object is returned (see the next member). The return value 0
indicates that currently no ostream object is tied to the ios
object. See section 6.5.5 for details.
std::ostream *ios::tie(std::ostream *outs):
The ostream object is tied to currentiosobject. This means that theostreamobject is flushed every time before an input or output action is performed by the currentiosobject. A pointer to theiosobject's originalostreamobject 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:streambuf object to which the stream
interfaces. See the member functions below for some examples.
ios::eofbit:ios object has sensed
end of file.
ios::failbit: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:
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 valuetrueis returned when the stream'sbadbithas been set andfalseotherwise. Iftrueis returned it indicates that an illegal operation has been requested at the level of thestreambufobject to which the stream interfaces. What does this mean? It indicates that thestreambufitself is behaving unexpectedly. Consider the following example:std::ostream error(0);Here an
ostreamobject is constructed without providing it with a workingstreambufobject. Since this `streambuf' will never operate properly, itsbadbitflag is raised from the very beginning:error.bad()returnstrue.
bool ios::eof():
the valuetrueis returned when end of file (EOF) has been sensed (i.e., theeofbitflag has been set) andfalseotherwise. Assume we're reading lines line-by-line fromcin, but the last line is not terminated by a final\ncharacter. In that casestd::getlineattempting to read the\ndelimiter hits end-of-file first. This raises theeofbitflag andcin.eof()returnstrue. For example, assumestd::string strandmainexecuting the statements:getline(cin, str); cout << cin.eof();Then
echo "hello world" | programprints the value 0 (no EOF sensed). But after
echo -n "hello world" | programthe value 1 (EOF sensed) is printed.
bool ios::fail():
the valuetrueis returned whenbadreturnstrueor when thefailbitflag was set. The valuefalseis 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 anothergetlineresults in raising thefailbitflag, causingcin::fail()to returntrue. In general:failreturnstrueif the requested stream operation failed. A simple example showing this consists of an attempt to extract anintwhen the input stream contains the texthello world. The valuenot fail()is returned by theboolinterpretation of a stream object (see below).
bool ios::good():
the value of thegoodbitflag is returned. It equalstruewhen 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,
helloandworld, while the second line is not terminated by a\ncharacter 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, extracting
xfails (goodreturningfalse). Then, the error state is cleared, and the first line is successfully read (goodreturningtrue). Finally the second line is read (incompletely):goodreturningfalse, andeofreturningtrue.
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, thenclearcan 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 aniosobject 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 the
goodbitflag 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 one
setstate()call use thebitoroperator: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):obj are copied to the current ios
object. The current ios object is returned.
ios::fill() const:ios::fill(char padding):
the padding character is redefined, the padding character that was used before the redefinition is returned. Instead of using this member function thesetfillmanipulator 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:bit_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 thebitoroperator. Example:// change the representation to hexadecimal cout.flags(ios::hex | cout.flags() & ~ios::dec);
int ios::precision() const: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 exceedssignifthen 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 thebitoroperator to combine multiple flags). Already set flags are not affected. The previous set of flags is returned. Instead of using this member function the manipulatorsetiosflagsmay 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 inmaskand 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 manipulatorresetiosflagsmay 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:std::left. Example:
cout.setf(ios::left, ios::adjustfield); cout << left; // same, using the manipulator cout << '\'' << setw(5) << "hi" << "'\n"; // displays 'hi '
ios::right: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: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:std::hex. Example:
cout.setf(ios::hex, ios::basefield); cout << hex; // same, using the manipulator cout << 16; // displays 10
ios::oct:std::oct. Example:
cout.setf(ios::oct, ios::basefield); cout << oct; // same, using the manipulator cout << 16; // displays 20
std::setbase(int radix):
cout << setbase(8); // octal numbers, use 10 for
// decimal, 16 for hexadecimal
cout << 16; // displays 20
Fine-tuning displaying values:
ios::boolalpha:true' for
the true logical value, and `false' for the false logical value
using boolalpha. By default this flag is not set. Complementary flag:
ios::noboolalpha. Manipulators: std::boolalpha and
std::noboolalpha. Example:
cout.setf(ios::boolalpha); cout << boolalpha; // same, using the manipulator cout << (1 == 1); // displays true
ios::showbase:0x prefix is used, with octal values the prefix 0. For the
(default) decimal value no particular prefix is used. Complementary flag:
ios::noshowbase. Manipulators: std::showbase and
std::noshowbase. Example:
cout.setf(ios::showbase); cout << showbase; // same, using the manipulator cout << hex << 16; // displays 0x10
ios::showpos:+ sign with positive decimal (only)
values. Complementary flag:ios::noshowpos. Manipulators:
std::showpos and std::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:ios::nouppercase. Manipulators:
std::uppercase and std::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), thefixedformatting 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 then
fixedhas the same effect.
ios::scientific:std::scientific. Example:
cout.setf(ios::scientific, ios::floatfield); cout << scientific; // same, using the manipulator cout << 12.25; // displays 1.22500e+01
ios::showpoint: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, 16
Note that the final 16 is an integral rather than a floating point number,
so it has no decimal point. So showpoint has no effect.
If ios::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 whitespace and flushing streams
std::endl:endl should be
avoided (in favor of inserting '\n') unless flushing 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:
std::flush:flush 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: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:unitbuf
should be avoided unless flushing 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:ios::noskipws has been set. Example
(assume the input contains 4 blank characters followed by the character
X):
cin >> ws; // skip whitespace 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 anostreamobject which is a wrapper around an existingstd::streambufobject. It isn't possible to define a plainostreamobject (e.g., usingstd::ostream out;) that can thereupon be used for insertions. Whencoutor its friends are used, we are actually using a predefinedostreamobject that has already been defined for us and interfaces to the standard output stream using a (also predefined)streambufobject handling the actual interfacing.It is, however, possible to define an
ostreamobject passing it a 0-pointer. Such an object cannot be used for insertions (i.e., it raises itsios::badflag when something is inserted into it), but it may be given astreambuflater. Thus it may be preliminary constructed, suspending its use until an appropriatestreambufbecomes available (see also section 14.8).
ostream class in C++ sources, the
<ostream> header file must be included. To use the predefined
ostream objects (std::cerr, std::cout etc.) the <iostream> header
file must be included.
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.
In addition to insertion operators ostreams also support
std::print. It is called as
std::ostream& os, std::format_string<Args...> fmt, Args&&... args
)
where ... indicates that any number of arguments can be specified.
However, since a print-like functionality is hardly ever required in
C++ programs it's not covered in detail in the C++ Annotations. Refer to
cppreference.com for details about std::print.
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):ostream& write(char const *buffer, int length):length bytes, stored in the char const
*buffer to the ostream object. Bytes are written as they are stored
in the buffer, no formatting is done whatsoever. Note that the first argument
is a char const *: a type cast is required to write any other
type. For example, to write an int as an unformatted series of
byte-values use:
int x; out.write(reinterpret_cast<char const *>(&x), sizeof(int));
The bytes written by the above 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():ostream &seekp(ios::off_type step, ios::seekdir org):off_type step representing the number of bytes the current stream
position is moved with respect to org. The step value
may be negative, zero or positive.
The origin of the step, org is a value in the
ios::seekdir enumeration. Its values are:
ios::beg:
ios::cur:tellp).
ios::end:EOF will pad the intermediate bytes with 0-valued bytes:
null-bytes.
Seeking before ios::beg raises the ios::fail flag.
Different from seekg (cf. section 6.5.1.2) seekp does not
clear the stream's ios::eofbit. To reset an ostream's state to `good',
its clear member should be called.
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():ostream object
is flushed to the device to which the ostream object interfaces. A stream
is flushed automatically when:
endl or flush manipulators (see section
6.3.2.2) are inserted into an ostream object;
close-operation is explicitly closed
(e.g., a std::ofstream object, cf. section 6.4.2).
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 anofstreamobject 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 anofstreamobject and associates it immediately with the file namednameusing 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.3.
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):ofstream object with an actual file. If the
ios::fail flag was set before calling open 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():ofstream object. The function sets the ios::fail
flag of the closed object. Closing the file flushes any buffered information
to the associated file. A file is automatically closed when the associated
ofstream object ceases to exist.
bool is_open() const:ofstream ostr was executed. When
we now check its status through good(), 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 returns true,
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:ios::ate below). The
file is created if it doesn't yet exist. When opening a stream in this mode
any existing content of the file is kept.
ios::ate:ofstream out("gone", ios::ate) rewrites the file gone,
because the implied ios::out causes the rewriting. If rewriting of an
existing file should be prevented, the ios::in
mode should be specified too. However, when ios::in is specified the
file must already exist. The ate mode only initially positions the file at
the end of file position. After that information may be written in the middle
of the file using seekp. When the app mode is used information is
only written at end of file (effectively ignoring seekp operations).
ios::binary:
ios::in:ios::out:ios::trunc:
in | out: The stream may be read and written. However, the
file must exist.
in | out | trunc: The stream may be read and written. It is
(re)created empty first.
An interesting subtlety is that the open members of the ifstream,
ofstream and fstream classes have a second parameter of type
ios::openmode. In contrast to this, the bitor operator returns an
int when applied to two enum-values. The question why the bitor
operator may nevertheless be used here is answered in a later chapter
(cf. section 11.13).
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):openmode as ios::ate, the
ostringstream object is initialized by the string init and remaining
insertions are appended to the content of the ostringstream object.
ostringstream ostr(ios::openmode mode = ios::out):ios::app). Example:
std::ostringstream out;
std::string str() const:ostringstream
object is returned.
void str(std::string const &str):ostringstream class:
several values are inserted into the object. Then, the text contained by the
ostringstream object is stored in a std::string, whose length and
content are thereupon printed. Such ostringstream objects are most often
used for doing `type to string' conversions,
like converting int values to text. Formatting flags can be used with
ostringstreams as well, as they are part of the ostream class.
Here is an example showing an ostringstream object being used:
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
ostringstream ostr("hello ", ios::ate);
cout << ostr.str() << '\n';
ostr.setf(ios::showbase);
ostr.setf(ios::hex, ios::basefield);
ostr << 12345;
cout << ostr.str() << '\n';
ostr << " -- ";
ostr.unsetf(ios::hex);
ostr << 12;
cout << ostr.str() << '\n';
ostr.str("new text");
cout << ostr.str() << '\n';
ostr.seekp(4, ios::beg);
ostr << "world";
cout << ostr.str() << '\n';
}
/*
Output from this program:
hello
hello 0x3039
hello 0x3039 -- 12
new text
new world
*/
std::put_time(std::tm const *specs, char const
*fmt) can be used to insert time specifications into std::ostream
objects.
Time specifications are provided in std::tm objects, and the way the time
should be displayed is defined by the format string fmt.
Starting with a chrono::time_point the following steps must be performed
to insert the time point's time into a std::ostream:
time_point (e.g.: system_clock{}.now());
to_time_t function, saving the
returned time_t value:
time_t secs = system_clock::to_time_t( system_clock{}.now() );
sec's address to either std::localtime or
std::gmtime. These functions return std::tm structs
containing the required time components expressed in, respectively, the
computer's local time or GMT;
localtime or gmtime together
with a format string (e.g., "%c") to put_time, inserting it into an
std::ostream:
// displays, e.g., Mon Nov 4 21:34:59 2019
time_t secs = system_clock::to_time_t( system_clock{}.now() );
std::cout << std::put_time(std::localtime(&secs), "%c") << '\n';
A simple function returning put_time's return value and expecting a
time_point and format string can be defined which handles the above two
statements. E.g., (omitting the std:: and std::chrono:: specifications
for brevity):
auto localTime(time_point<system_clock> const &tp, char const *fmt)
{
time_t secs = system_clock::to_time_t( tp );
return put_time(localtime(&secs), fmt);
}
// used as:
cout << localTime(system_clock{}.now(), "%c") << '\n';
Many more format specifiers are recognized by put_time. Specifiers start
with %. To display a percent character as part of the format string write
it twice: %%. In addition to the standard escape sequences, %n can be
used instead of \n, and %t can be used instead of \t.
| Year specifiers | ||||
| Specifier | Meaning | std::tm field(s) |
||
%Y |
year as a 4 digit decimal number |
tm_year |
||
%EY |
year in an alternative representation |
tm_year |
||
%y |
last 2 digits of year as a decimal number
(range [00,99]) |
tm_year |
||
%Oy |
last 2 digits of year using an alternative numeric
system |
tm_year |
||
%Ey |
year as offset from locale's alternative calendar
period %EC (locale-dependent) |
tm_year |
||
%C |
first 2 digits of year as a decimal number
(range [00,99]) |
tm_year |
||
%EC |
name of the base year (period) in the locale's
alternative representation |
tm_year |
||
%G |
ISO 8601 week-based year, i.e. the year that contains
the specified week |
tm_year, tm_wday, tm_yday |
||
%g |
last 2 digits of ISO 8601 week-based year
(range [00,99]) |
tm_year, tm_wday, tm_yday |
||
| Month specifiers | ||||
| Specifier | Meaning | std::tm field(s) |
||
%b |
abbreviated month name, e.g. Oct |
tm_mon |
||
%m |
month as a decimal number (range [01,12]) |
tm_mon |
||
%Om |
month using an alternative numeric system |
tm_mon |
||
| Week specifiers | ||||
| Specifier | Meaning | std::tm field(s) |
||
%U |
week of the year as a decimal number (Sunday is the first day
of the week) (range [00,53]) |
tm_year, tm_wday, tm_yday |
||
%OU |
week of the year, as by %U, using an alternative numeric
system |
tm_year, tm_wday, tm_yday |
||
%W |
week of the year as a decimal number (Monday is the first day
of the week) (range [00,53]) |
tm_year, tm_wday, tm_yday |
||
%OW |
week of the year, as by %W, using an alternative numeric
system |
tm_year, tm_wday, tm_yday |
||
%V |
ISO 8601 week of the year (range [01,53]) |
tm_year, tm_wday, tm_yday |
||
%OV |
week of the year, as by %V, using an alternative numeric
system |
tm_year, tm_wday, tm_yday |
||
| Day of the year/month specifiers | ||||
| Specifier | Meaning | std::tm field(s) |
||
%j |
day of the year as a decimal number (range
[001,366]) |
tm_yday |
||
%d |
day of the month as a decimal number (range [01,31]) |
tm_mday |
||
%Od |
zero-based day of the month using an alternative numeric
system |
tm_mday |
||
%e |
day of the month as a decimal number (range [1,31]) |
tm_mday |
||
%Oe |
one-based day of the month using an alternative numeric
system |
tm_mday |
||
| Day of the week specifiers | ||||
| Specifier | Meaning | std::tm field(s) |
||
%a |
abbreviated weekday name, e.g. Fri |
tm_wday |
||
%A |
full weekday name, e.g. Friday |
tm_wday |
||
%w |
weekday as a decimal number, where Sunday is 0 (range
[0-6]) |
tm_wday |
||
%Ow |
weekday, where Sunday is 0, using an alternative numeric
system |
tm_wday |
||
%u |
weekday as a decimal number, where Monday is 1 (ISO 8601
format) (range [1-7]) |
tm_wday |
||
%Ou |
weekday, where Monday is 1, using an alternative numeric
system |
tm_wday |
||
| Hour, minute, second specifiers | ||||
| Specifier | Meaning | std::tm field(s) |
||
%H |
hour as a decimal number, 24 hour clock (range
[00-23]) |
tm_hour |
||
%OH |
hour from 24-hour clock using an alternative numeric
system |
tm_hour |
||
%I |
hour as a decimal number, 12 hour clock (range
[01,12]) |
tm_hour |
||
%OI |
hour from 12-hour clock using the alternative numeric
system |
tm_hour |
||
%M |
minute as a decimal number (range [00,59]) |
tm_min |
||
%OM |
minute using an alternative numeric system |
tm_min |
||
%S |
second as a decimal number (range [00,60]) |
tm_sec |
||
%OS |
second using an alternative numeric system |
tm_sec |
||
| Additional specifiers | ||||
| Specifier | Meaning | std::tm field(s) |
||
%c |
standard date and time string, e.g. Sun Oct 17 04:41:13
2010 |
all |
||
%Ec |
alternative date and time string |
all |
||
%x |
localized date representation |
all |
||
%Ex |
alternative date representation |
all |
||
%X |
localized time representation |
all |
||
%EX |
alternative time representation |
all |
||
%D |
equivalent to "%m/%d/%y" |
tm_mon, tm_mday, tm_year |
||
%F |
equivalent to "%Y-%m-%d" (the ISO 8601 date
format) |
tm_mon, tm_mday, tm_year |
||
%r |
localized 12-hour clock time |
tm_hour, tm_min, tm_sec |
||
%R |
equivalent to "%H:%M" |
tm_hour, tm_min |
||
%T |
equivalent to "%H:%M:%S" (the ISO 8601 time
format) |
tm_hour, tm_min, tm_sec |
||
%p |
localized a.m. or p.m. |
tm_hour |
||
%z |
offset from UTC in the ISO 8601 format (e.g. -0430; no characters if time zone information is not available) |
tm_isdst |
||
%Z |
time zone name or abbreviation (no characters if time zone information is not available) |
tm_isdst |
||
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 extract information from
memory rather than from file (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::streambufobject. Similarly toostreamobjects,istreamobjects may be defined by passing it initially a 0-pointer. See section 6.4.1 for a discussion, see also section 14.8, and see chapter 26 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 to receive new values. The normal associativity of >> remains unaltered, so when a statement like
cin >> x >> y;
is encountered, the leftmost two operands are evaluated first (cin
>> x), and an istream & object, which is actually the same
cin object, is returned. Now, the statement is reduced to
cin >> y
and the y variable is extracted from cin.
The >> operator has many (overloaded) variants and thus many types of
variables can be extracted from istream objects. There is an overloaded
>> available for the extraction of an int, of a double, of a
string, of an array of characters, possibly to the location pointed at by a
pointer, etc., etc.. String or character array extraction
by default first skips all whitespace characters, and then extracts
all consecutive non-whitespace characters. Once an extraction operator has
been processed the istream object from which the information was extracted
is returned and it can immediately be used for additional istream
operations that appear in the same expression.
Streams do not support facilities for formatted input as offered by
C's scanf and vscanf functions. Although it is not difficult to
add such facilities to the world of streams, scanf-like functionality is
in practice never needed in C++ programs. Furthermore, as it is
potentially type-unsafe, it is better to avoid using C-type formatted
input.
When binary files must be read, the information should
normally not be formatted: an int value should be read as a series of
unaltered bytes, not as a series of ASCII numeric characters 0 to 9. The
following member functions for reading information from istream objects
are available:
int gcount() const:int get():char value using an int return type.
EOF is returned if no more character are available.
istream &get(char &ch):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'):len - 1 characters are read from the input
stream into the array starting at buffer, which should be at least len
bytes long. Reading also stops when the delimiter delim is
encountered. However, the delimiter itself is not removed from the input
stream.
Having stored the characters into buffer, a 0-valued character is
written beyond the last character stored into the buffer. The functions
eof and fail (see section 6.3.1) return 0 (false) if the
delimiter was encountered before reading len - 1 characters or if the
delimiter was not encountered after reading len - 1 characters. It is OK
to specifiy a 0-valued character delimiter: this way NTBSs may be read from a
(binary) file.
istream &getline(char *buffer, int len, char delim = '\n'):get member
function, but getline removes delim from the stream if it is actually
encountered. The delimiter itself, if encountered, is not stored in the
buffer. If delim was not found (before reading len - 1
characters) the fail member function, and possibly also eof returns
true. Realize that the std::string class also offers a function
std::getline which is generally preferred over this getline member
function that is described here (see section 5.2.4).
istream &ignore():istream &ignore(int n):n characters are skipped from the input stream.
istream &ignore(int n, int delim):n characters are skipped but skipping characters
stops after having removed delim from the input stream.
int peek():EOF
is returned if no more characters are available.
istream &putback(char ch):ch 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):len bytes are read from the input stream into the
buffer. If EOF is encountered first, fewer bytes are read, with the
member function eof returning true. 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 function gcount() may be
used to determine the number of characters that were retrieved by read.
istream &readsome(char *buffer, int len):len bytes are read from the input stream into the
buffer. All available characters are read into the buffer, but if EOF
is encountered, fewer bytes are read, without setting the ios::eofbit
or ios::failbit.
istream &unget():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():istream &seekg(ios::off_type step, ios::seekdir org):off_type step representing the number of bytes the current stream
position is moved with respect to org. The step value may be negative,
zero or positive.
The origin of the step, org is a value in the
ios::seekdir enumeration. Its values are:
ios::beg:
ios::cur:tellp).
ios::end:ios::beg raises the ios::failbit
flag.
Calling seekg clears the istream's ios::failbit, but not its
ios::badbit or ios::badbit. To ensure that the stream's state is reset
to `good' its member clear should be called.
To illustrate: in the following example cin's ios::eofbit is
set. Following seekg that bit is cleared, but its ios::goodbit stil
isn't set. Since its goodbit isn't set, extraction fails following
seekg:
int main() // in 'src.cc'
{
cin.setstate(ios::eofbit | ios::failbit);
cerr << cin.good() << ' ' << cin.eof() << '\n';
cin.seekg(0);
cerr << cin.good() << ' ' << cin.eof() << '\n' <<
(cin.get() == EOF ? "failed" : "OK") << '\n';
cin.clear();
cerr << cin.good() << ' ' << cin.eof() << '\n' <<
(cin.get() == EOF ? "failed" : "OK") << '\n';
}
outputs when called as 'a.out < src.cc':
0 1
0 0
failed
1 0
OK
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 anifstreamobject 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 anifstreamobject and associate it immediately with the file namednameusing 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):ifstream object with an actual file. If the
ios::fail flag was set before calling open 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():ifstream object. The function sets the ios::fail
flag of the closed object. Closing the file flushes any buffered information
to the associated file. A file is automatically closed when the associated
ifstream object ceases to exist.
bool is_open() const:ifstream ostr was executed. When
we now check its status through good(), 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 returns true,
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):init's content
istringstream istr(ios::openmode mode = ios::in):std::istringstream in;
void str(std::string const &str):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 content into an ostream:
#include <iostream>
using namespace std;
int main()
{
cin.ignore(80, '\n'); // skip the first line and...
cout << cin.rdbuf(); // copy the rest through the streambuf *
}
This way of copying streams only assumes the existence of a streambuf
object. Consequently it can be used with all specializations of the
streambuf class.
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 fail: 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 26.2.3).
A common situation where redirection is useful is when error messages should
be written to file rather than to the standard error stream, usually indicated
by its file descriptor number 2. In the Unix operating system using the
bash shell, this can be realized as follows:
program 2>/tmp/error.log
Following this command any error messages written by program are
written to /tmp/error.log, instead of appearing on the screen.
Here is an example showing how this can be implemented using streambuf
objects. Assume program expects an argument defining the name
of the file to write the error messages to. It could be called as follows:
program /tmp/error.log
The program looks like this, an explanation is provided below the program's source text:
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char **argv)
{
ofstream errlog; // 1
streambuf *cerr_buffer = 0; // 2
if (argc == 2)
{
errlog.open(argv[1]); // 3
cerr_buffer = cerr.rdbuf(errlog.rdbuf()); // 4
}
else
{
cerr << "Missing log filename\n";
return 1;
}
cerr << "Several messages to stderr, msg 1\n";
cerr << "Several messages to stderr, msg 2\n";
cout << "Now inspect the contents of " <<
argv[1] << "... [Enter] ";
cin.get(); // 5
cerr << "Several messages to stderr, msg 3\n";
cerr.rdbuf(cerr_buffer); // 6
cerr << "Done\n"; // 7
}
/*
Generated output on file argv[1]
at cin.get():
Several messages to stderr, msg 1
Several messages to stderr, msg 2
at the end of the program:
Several messages to stderr, msg 1
Several messages to stderr, msg 2
Several messages to stderr, msg 3
*/
errlog is the
ofstream to write the error messages to, 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::iostream
objects. Commonly encountered are std::fstream objects and
sometimes std::stringstream objects. Other types of
readable and writable streams can be defined, by deriving such streams from
the std::iostream class (cf. chapter 14, in particular its
section 14.8.2).
In this section we concentrate on the std::fstream class. As with
ifstream and ofstream objects, the fstream constructor expects the
name of the file to be opened:
fstream inout("iofile", ios::in | ios::out);
Note the use of the constants ios::in and ios::out,
indicating that the file must be opened for both reading and writing. Multiple
mode indicators may be used, concatenated by the bitor operator.
Alternatively, instead of ios::out, ios::app could have been
used and mere writing would become appending (at the end of the file).
Reading and writing to the same stream is always a bit awkward: what to do when the stream may not yet exist, but if it already exists it should not be rewritten? To realize this the following approach can be used:
#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 construction fails fname didn't yet
exist. But then, after clearing the failure flag, open is used also
specifying the ios::trunc flag: this creates an empty file, but
because of ios::in the file is also readable. In addition
ios::ate could be specified, ensuring that the initial read/write
action would by default occur 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 (in general: iostream) objects, combinations of file
flags are used to make sure that a stream is or is not (re)created empty when
opened. See section 6.4.2.1 for details.
Once a file has been opened in read and write mode, the << operator
can be used to insert information into the file, while the >> operator
may be used to extract information from the file. These operations may be
performed in any order, but a seekg or seekp operation is required
when switching between insertions and extractions. The seek operation is used
to activate the stream's data used for reading or those used for writing (and
vice versa). The istream and ostream parts of fstream objects
share the stream's data buffer and by performing the seek operation the stream
either activates its istream or its ostream part. If the seek is
omitted, reading after writing and writing after reading simply fails. The
example shows a whitespace-delimited word being read from a file, writing
another string to the file, just beyond the point where the just read word
terminated. Finally yet another string is read which is found just beyond the
location where the just written strings ended:
fstream f("filename", ios::in | ios::out);
string str;
f >> str; // read the first word
// write a well-known text
f.seekp(0, ios::cur);
f << "hello world";
f.seekg(0, ios::cur);
f >> str; // and read again
Since a seek or clear operation is required when alternating between read and write (extraction and insertion) operations on the same file it is not possible to execute a series of << and >> operations in one expression statement.
Of course, random insertions and extractions are hardly ever used. Generally,
insertions and extractions occur at well-known locations in a file. In those
cases, the position where insertions or extractions are required can be
controlled and monitored by the seekg, seekp, tellg and tellp
members (see sections 6.4.1.2 and 6.5.1.2).
Error conditions (see section 6.3.1) occurring due to, e.g., reading
beyond end of file, reaching end of file, or positioning before begin of file,
can be cleared by the clear member function. Following clear
processing may continue. E.g.,
fstream f("filename", ios::in | ios::out);
string str;
f.seekg(-10); // this fails, but...
f.clear(); // processing f continues
f >> str; // read the first word
A situation where files are both read and written is seen in
database applications, using files consisting of records having fixed
sizes, and where locations and sizes of pieces of information are known. For
example, the following program adds text lines to a (possibly existing)
file. It can also be used to retrieve a particular line, given its
order-number in the file. A binary file index allows for the quick
retrieval of the location of lines.
#include <iostream>
#include <fstream>
#include <string>
#include <climits>
using namespace std;
void err(char const *msg)
{
cout << msg << '\n';
}
void err(char const *msg, long value)
{
cout << msg << value << '\n';
}
void read(fstream &index, fstream &strings)
{
int idx;
if (!(cin >> idx)) // read index
{
cin.clear(); // allow reading again
cin.ignore(INT_MAX, '\n'); // skip the line
return err("line number expected");
}
index.seekg(idx * sizeof(long)); // go to index-offset
long offset;
if
(
!index.read // read the line-offset
(
reinterpret_cast<char *>(&offset),
sizeof(long)
)
)
return err("no offset for line", idx);
if (!strings.seekg(offset)) // go to the line's offset
return err("can't get string offset ", offset);
string line;
if (!getline(strings, line)) // read the line
return err("no line at ", offset);
cout << "Got line: " << line << '\n'; // show the line
}
void write(fstream &index, fstream &strings)
{
string line;
if (!getline(cin, line)) // read the line
return err("line missing");
strings.seekp(0, ios::end); // to strings
index.seekp(0, ios::end); // to index
long offset = strings.tellp();
if
(
!index.write // write the offset to index
(
reinterpret_cast<char *>(&offset),
sizeof(long)
)
)
return err("Writing failed to index: ", offset);
if (!(strings << line << '\n')) // write the line itself
return err("Writing to `strings' failed");
// confirm writing the line
cout << "Write at offset " << offset << " line: " << line << '\n';
}
int main()
{
fstream index("index", ios::trunc | ios::in | ios::out);
fstream strings("strings", ios::trunc | ios::in | ios::out);
cout << "enter `r <number>' to read line <number> or "
"w <line>' to write a line\n"
"or enter `q' to quit.\n";
while (true)
{
cout << "r <nr>, w <line>, q ? "; // show prompt
index.clear();
strings.clear();
string cmd;
cin >> cmd; // read cmd
if (cmd == "q") // process the cmd.
return 0;
if (cmd == "r")
read(index, strings);
else if (cmd == "w")
write(index, strings);
else if (cin.eof())
{
cout << "\n"
"Unexpected end-of-file\n";
return 1;
}
else
cout << "Unknown command: " << cmd << '\n';
}
}
Another example showing reading and writing of files is provided by
the next program. It also illustrates the processing of NTBSs:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{ // r/w the file
fstream f("hello", ios::in | ios::out | ios::trunc);
f.write("hello", 6); // write 2 NTB strings
f.write("hello", 6);
f.seekg(0, ios::beg); // reset to begin of file
char buffer[100]; // or: char *buffer = new char[100]
char c;
// read the first `hello'
cout << f.get(buffer, sizeof(buffer), 0).tellg() << '\n';
f >> c; // read the NTB delim
// and read the second `hello'
cout << f.get(buffer + 6, sizeof(buffer) - 6, 0).tellg() << '\n';
buffer[5] = ' '; // change asciiz to ' '
cout << buffer << '\n'; // show 2 times `hello'
}
/*
Generated output:
5
11
hello hello
*/
A completely different way to read and write streams may be implemented
using streambuf members. All considerations mentioned so far remain valid
(e.g., before a read operation following a write operation seekg must be
used). When streambuf objects are used, either an
istream is associated with the streambuf object of another ostream
object, or an ostream object is associated with the
streambuf object of another istream object. Here is the previous
program again, now using
associated streams:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void err(char const *msg); // see earlier example
void err(char const *msg, long value);
void read(istream &index, istream &strings)
{
index.clear();
strings.clear();
// insert the body of the read() function of the earlier example
}
void write(ostream &index, ostream &strings)
{
index.clear();
strings.clear();
// insert the body of the write() function of the earlier example
}
int main()
{
ifstream index_in("index", ios::trunc | ios::in | ios::out);
ifstream strings_in("strings", ios::trunc | ios::in | ios::out);
ostream index_out(index_in.rdbuf());
ostream strings_out(strings_in.rdbuf());
cout << "enter `r <number>' to read line <number> or "
"w <line>' to write a line\n"
"or enter `q' to quit.\n";
while (true)
{
cout << "r <nr>, w <line>, q ? "; // show prompt
string cmd;
cin >> cmd; // read cmd
if (cmd == "q") // process the cmd.
return 0;
if (cmd == "r")
read(index_in, strings_in);
else if (cmd == "w")
write(index_out, strings_out);
else
cout << "Unknown command: " << cmd << '\n';
}
}
In this example
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.3) 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. However, instead of using two objects, defining/using a iostream
object receiving the streambuf automatically ensures that positioning for
reading and writing is synchronized (see also section 14.8.2).
streambuf used by
an iostream object over using a predefined fstream object is that the
stream doesn't have to be a named file (see also section 14.8).
Like fstream objects string-stream objects can also be used for reading
and writing. After including the <sstream> header file a
std::stringstream can be defined which supports both
reading and writing. After inserting information into a stringstream
object seekg(0) can be called to read its info from the beginning of its
content. When a stringstream must repeatedly be used for reading and
writing call its clear and str members before starting a new writing
cycle. Alternatively, a stringstream str can be reinitialized using str
= stringstream{}. Here is an example:
#include <iostream>
#include <sstream>
using namespace std;
int main(int argc, char **argv)
{
stringstream io;
for (size_t redo = 0; redo != 2; ++redo)
{
io.clear(); // clears the not-good flags
io.str("");
io << argv[0] << '\n';
io.seekg(0);
string line;
while (getline(io, line)) // results in io.eof()
cout << line << '\n';
}
}