We're always interested in getting feedback. E-mail us if you like this guide, if you think that important material is omitted, if you encounter errors in the code examples or in the documentation, if you find any typos, or generally just if you feel like e-mailing. Mail to Frank Brokken or use an e-mail form. Please state the concerned document version, found in the title. If you're interested in a printable PostScript copy, pick up your own copy inzip
-format byftp
from ftp.icce.rug.nl/pub/http.
As an extension to the standard stream (FILE
) approach well known from
the C programming language, C++ offers an I/O library based on
class
concepts.
Earlier (in chapter 3) we've already
seen examples of the use of the C++ I/O library. In this chapter
we'll cover the library to a larger extent.
Apart from defining the insertion (<<) and extraction(>>) operators,
the use of the C++ I/O library offers the additional advantage
of type safety in all kinds of standard situations. Objects (or plain
values) are inserted into the iostreams. 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 uses the objects where
their values should appear, as in
cout << "There were " << nMaidens << " virgins present\n";
The compiler notices the type of the nMaidens
variable, inserting
its proper value at the appropriate place in the sentence inserted into
the cout
iostream.
Compare this to the situation encountered in C. Although C compilers
are getting smarter and smarter over the years, and although a well-designed
C compiler may warn you for a mismatch between a format specifier and the
type of a variable encountered in the corresponding position of the argument
list of a printf()
statement, it can't do much more than warn you.
The type safety seen in C++ prevents you from making type
mismatches, as there are no types to match.
Apart from this, the iostreams offer more or less the same set of
possibilities as the standard streams of C: files can be
opened, closed, positioned, read, written, etc.. The remainder of this
chapter presents an overview.
In general, input is managed by istream
objects, having the derived
classes ifstream
for files, and istrstream
for strings (character
arrays), whereas
output is managed by ostream
objects, having the derived classes
ofstream
for files and ostrstream
for strings.
If a file should allow both reading from and writing to, a fstream
object
should be used.
Finally, in order to use the iostream
facilities, the header file
iostream.h
must be included in source files using these facilities.
<<
) points to the ostream
object wherein
the information is inserted. The extraction operator points to the
object receiving the information obtained from the istream
object.
As an example, the <<
operator as defined with the class ostream
is an overloaded operator having as prototype, e.g.,
ostream &ostream::operator <<(char const *text)
The normal associativity of the <<
-operator remains unaltered, so
when a statement like
cout << "hello "
), and a ostream &
object, which is actually the
same cout
object. From here, the statement is reduced to
cout
.
Since the <<
operator has a lot of (overloaded) variants, many types of
variables can be inserted into ostream
objects. There is an overloaded
<<
-operator expecting an int
, a double
, a pointer, etc. etc..
For every part of the information that is inserted into the stream the operator
returns the ostream
object into which the information so far was inserted,
and the next part of the information to be inserted is devoured.
As we have seen in the discussion of friends, even new classes can
contain an overloaded <<
operator to be used with ostream
objects
(see sections 10.3 and 10.3.1).
Consider the following code example:
#include <iostream.h> int main() { int value = 15, *p = &value; cout << "Value: " << value << "\n" << "via p: " << *p << "\n" << "value's address: " << &value << "\n" << "address via p: " << p << "\n" << "p's address: " << &p << "\n"; }In this form the following output is generated (gnu C++ compiler, version 2.7.2):
Value: 15 via p: 15 value's address: 1 address via p: 1 p's address: 1
This is a bit unexpected. How to get the addresses? By using an explicit
cast to the generic pointer void *
the problem is solved:
#include <iostream.h> int main() { int value = 15, *p = &value; cout << "Value: " << value << "\n" << "via p: " << *p << "\n" << "value's address: " << (void *)&value << "\n" << "address via p: " << (void *)p << "\n" << "p's address: " << (void *)&p << "\n"; }
The above code produces, e.g.,
Value: 15 via p: 15 value's address: 0x804a1e4 address via p: 0x804a1e4 p's address: 0xbffff9fc
scanf()
function. I.e., white space characters are skipped. Also,
the operator doesn't expect pointers to variables to be given new values, but
references (with the exception of the char *
).
Consider the following code:
int i1, i2; char c; cin >> i1 >> i2; // see (1) while (cin >> c && c != '.') // see (2) process(c); char // see (3) buffer[80]; // see (3) while (cin >> buffer) process(buffer);
This example shows several characteristics of the extraction operator worth
noting. Assume the input consists of the following lines:
125 22 h e l l o w o r l d . this example shows that we're not yet done with C++
i1
and i2
.
White-space (newlines, spaces, tabs) is skipped, and the values
125 and 22 are assigned to i1
and i2
.
If the assignment fails, e.g., when there are no numbers to be
converted, the result of the extraction operator evaluates to a zero
result, which can be used for testing purposes, as in:
if (!(cin >> i1))
hello
and world
are
produced by cin
, but the blanks that appear in between are not.
Furthermore, the final '.'
is not processed, since that one's
used as a sentinel: the delimiter to end the while
-loop, when the
extraction is still successful.
char *
is passed, white-space
delimited strings are extracted. So, here the words this, example,
shows, that, we're, not, yet, done, with
and C++
are returned.
Then, the end of the information is reached. This has two consequences:
First, the while
-loop terminates. Second, an empty string is
copied into the buffer
variable.
stdin
, the standard input
stream, normally connected to the keyboard, stdout
, the (buffered) standard
output stream, normally connected to the screen, and stderr
, the
(unbuffered) standard error stream, normally not redirected, and also connected
to the screen.
In C++ comparable iostreams are
cin
, an istream
object from which information can be
extracted. This stream is normally connected to the keyboard.
cout
, an ostream
object, into which information can be
inserted. This stream is normally connected to the screen.
cerr
, an ostream
object, into which information can be
inserted. This stream is normally connected to the screen. Insertions
into that stream are unbuffered.
clog
, an ostream
object, comparable to cerr
, but using
buffered insertions. Again, this stream is normally connected to the
screen.
fstream
objects, two header files must be
included: iostream.h
and fstream.h
. Files to read are accessed through
ifstream
objects, files to write are accessed through ofstream
objects.
Files may be accessed for reading and writing as well. The general fstream
object is used for that purpose.
Apart from the iostream.h
header file the headerfile fstream.h
must be
included when an fstream, ofstream,
or ifstream
object must be
constructed or used.
ofstream
object must be created,
the constructor receiving the name of the file to be opened:
ofstream out("outfile");
By default this will result in the creation of the file, and information
inserted into it will be written from the beginning of the file. Actually,
this corresponds to the creation of the ofstream
object in standard output
mode, for which the enumeration value ios::out
could have been provided as
well:
ofstream out("outfile", ios::out);
Alternatively, instead of (re)writing the file, the ofstream
object could be
created in the append mode, using the ios::app
mode indicator:
ofstream out("outfile", ios::app);
Normally, information will be inserted into the ofstream
object using the
insertion operator <<
, in the way it is used with the standard streams
like cout
, e.g.:
out << "Information inserted into the 'out' stream\n";
Just like the fopen()
function of C may fail, the construction of the
ofstream
object might not succeed. When an attempt is made to
create an ofstream
object, it is a good idea to test the successful
construction. The ofstream
object returns 0 if its construction failed.
This value can be used in tests, and the code can throw an exception (see
chapter 13) or it can handle the failure itself, as in the
following code:
#include <iostream.h> #include <fstream.h> int main() { ofstream out("/"); // creating 'out' fails if (!out) { cerr << "creating ofstream object failed\n"; exit(1); } }
ifstream
object must be created,
the constructor receiving the name of the file to be opened:
ifstream in("infile");
By default this will result in the opening of the file for reading. The file
must exist for the ifstream
object construction to succeed.
Instead of the shorthand form to open a file for reading, and explicit ios
flag may be used as well:
ifstream in("infile", ios::in);
Normally, information will be extracted from the ifstream
object using the
extraction operator >>
, in the way it is used with the standard stream
cin
, e.g.:
in >> x >> y;
The extraction operator skips blanks: between words, between characters,
between numbers, etc.. Consequently, if the input consists of the following
information:
12 13 a b hello worldthen the next code fragment will read
12
and 13
into x
and y
,
will then return the characters a
and b
, and will finally read
hello
and world
into the character array buffer
:
int x, y; char c, buffer[10]; cin >> x >> y >> c >> c >> buffer >> buffer;Notice that no format specifiers are necessary. The type of the variables receiving the extracted information determines the nature of the extraction: integer values for
int
s, white space delimited strings for char []
s,
etc..
Just like the fopen()
function of C may fail, the construction of the
ifstream
object might not succeed. When an attempt is made to
create an ifstream
object, it is a good idea to test the successful
construction. The ifstream
object returns 0 if its construction failed.
This value can be used in tests, and the code can throw an exception (see
section 13) or it can handle the failure itself, as in the
following code:
#include <iostream.h> #include <fstream.h> int main() { ifstream in(""); // creating 'in' fails if (!in) { cerr << "creating ifstream object failed\n"; exit(1); } }
fstream
object
must be created. Again, the constructor receives the name of the file to be
opened:
fstream inout("infile", ios::in | ios::out);
Note the use of the ios
constants ios::in
and ios::out
, indicating
that the file must be opened both for reading and writing. Multiple mode
indicators may be used, concatenated by the binary or operator '|'
.
Alternatively, instead of ios::out
,
ios::app
might have been used, in which case writing will always be done
at the end of the file.
With fstream
objects, the ios::out
will result in the creation
of the file, if the file doesn't exist, and if ios::out
is the only
mode specification of the file. If the mode ios::in
is given as well,
then the file is created only if it doesn't exist. So, we have the following
possibilities:
------------------------------------------------------------- Specified Filemode --------------------------------------------- File: ios::out ios::in | ios::out ------------------------------------------------------------- exists File is rewritten File is used as found doesn't exist File is created File is created -------------------------------------------------------------
Once a file has been opened in read and write mode, the <<
operator
may be used to write to the file, while the >>
operator may be used
to read from the file. These operations may be performed in random order.
The following fragment will read a blank-delimited word from the file,
will write a string to the file, just beyond the point where the string
just read terminated, and will read another string: just beyond the location
where the string just written ended:
... fstream f("filename", ios::in | ios::out); char buffer[80]; // for now assume this // is long enough f >> buffer; // read the first word // write a well known text f << "hello world"; f >> buffer; // and read againSince the operators
<<
and >>
can apparently be used with fstream
objects, you might wonder whether a series of <<
and >>
operators
in one statement might be possible. After all, f >> buffer
should produce
a fstream &
, shouldn't it?
The answer is: it doesn't. The compiler casts the fstream
object into
an ifstream
object in combination with the extraction operator, and into an
ofstream
object in combination with the insertion operator. Consequently,
a statement like
f >> buffer << "grandpa" >> buffer;
no match for `operator <<(class istream, char[8])'
istream
class, the fstream
object is apparently considered an ifstream
object in combination with
the extraction operator.
Of course, random insertions and extractions are hardly used. Generally,
insertions and extractions take place at specific locations in the file.
In those cases, the position where the insertion or extraction must take
place can be controlled and monitored by the seekg()
and tellg()
memberfunctions. The memberfunction seekg()
expects two arguments,
the second one having a default value:
seekg(long offset, seek_dir position = ios::beg);
long
offset with respect to a seek_dir
postion.
The seek_dir
position may be one of:
ios::beg
: add offset
to the begin of file position. Negative
offsets result in an error condition, which must be cleared before
any further operations on the file will succeed.
ios::end
: add offset
to the end of file position. Positive
offsets result in the insertion of as many padding (char)0
characters as necessary to reach the intended offset.
ios::cur
: add offset
to the current file position. If adding
the offset
to the current position would result in a position
before ios::beg
, then, again, an error condition results. If the
position would be beyond ios::end
, then extra (char)0
characters are supplied.
Error conditions (see also section 9.3.4) occurring
due to, e.g., reading beyond end of file, reaching end of file, or positioning
before begin of file, can be cleared using the clear()
memberfunction.
Following clear()
processing continues. E.g.,
... fstream f("filename", ios::in | ios::out); char buffer[80]; // for now assume this // is long enough f.seekg(-10); // this fails, but... f.clear(); // processing f continues f >> buffer; // read the first word
Several condition member functions of the fstreams
exist to manipulate
the states of the stream:
bad()
: this member function returns a non-zero value when an invalid
operation has been requested, like seeking before the begin of file
position.
eof()
: this member function returns a non-zero value when the stream
has reached
.
fail()
: this member function returns a non-zero value when
eof()
or bad()
returns a non-zero value.
good()
, on the other hand,
returns a non-zero value when there are no error conditions. Alternatively,
the operator '!'
could be used for that in combination with fail()
. So
good()
and !fail()
return identical logical values.
A subtlety is the following: Assume a stream is constructed, but not attached
to an actual file. E.g., the statement ifstream instream
creates the
stream object, but doesn't assign it to a file. However, if we next
check it's status through good()
this member will return a non-zero value.
The `good' status here indicates that the stream object has been cleanly
constructed. It doesn't mean the file is also open. A direct test for that
can be performed by inspecting instream.rdbuf()->is_open
. If non-zero,
the stream is open.
When an error condition has occurred (i.e., fail()
returns a non-zero
value), and can be repaired, then the member
function clear()
should be called to clear the error status of the file.
stream
objects
which are worthwhile mentioning.
gcount()
: this function returns the number of characters read by
getline()
(described below) or read()
(described below).
flush()
: this function flushed the output of the ostream
object.
get()
: returns the next character as an int
: End-of-file is
returned as
, a value which can't be a character.
get(char c)
: this function reads a char
from an istream
object, and returns the istream
object for which the function
was called.get()
and get(char c)
functions read separate characters,
and will not skip whitespace.
getline(char *buffer, int size, int delimiter = '\n')
:
this function
reads up to size - 1
characters or until delimiter
was read
into buffer
, and appends a final ascii-z
. The delimiter is not
entered into buffer
. The function changes the state of the
output-stream to fail if a line was not terminated by
the delimiter. Since this situation will prevent the function
from reading more information, the function clear
must be
called in these circumstances to allow the function to produce
more information. The frame for reading lines from an
istream
object is, therefore:
#include <iostream.h> int main() { char buffer[100]; while (1) { cin.getline(buffer, 100); cout << buffer; if (cin.eof()) return(0); if (cin.good()) cout << endl; else cin.clear(); } }
istream &ignore([int n] [, int delimiter])
. This function
skips over a certain number of characters, but not beyond the
delimiter
character. By default, the delimiter
character is
: the function ignore()
will not skip
beyond
. If the number of characters isn't specified,
one character will be skipped.
int peek()
. This function returns the character that will be
read with the next call to the function get()
.
istream &putback(char c)
. This function attempts to put
character c
back into the stream. The most recently read
character character may always be returned into the stream. If
the character can't be returned,
is returned. This
function is the analogue of C's ungetc()
function.
int opfx()
. This function should be called before any further
processing. If the ostream
object is in the state `good',
flush()
is called for that object, and 1 is returned. Otherwise,
0 is returned. The p
in opfx()
indicates prefix: the
function should be called before processing the ostream
object.
int osfx()
: This function is the suffix equivalent for opfx()
.
called at the conclusion of any processing.
All the ostream
methods end by calling osfx()
. unitbuf
flag is set for this stream, osfx()
flushes any
buffered output for it, while any
output buffered for the C
output streams stdout
and stderr
files is flushed if the stdio
flag was set for this stream.
read(char *buffer, int size)
: this function reads
size
bytes from the istream
object calling this memberfunction
into buffer
.
write(char const *str, int length)
: writes length
characters in
str
to the ostream
object for which it was called, and it
returns the ostream
object.
iostreams
, there
are situations in which special formatting is required. Formatting may
involve the control of the width of an output field or an input buffer
or the form (e.g., the radix) in which a value is displayed. The
functions (v)form()
and (v)scan()
can be used for special formatting.
Apart from these memberfunctions, memberfunctions are available for defining
the precision and the way numbers are displayed. Apart from using members,
manipulators exist for controlling the display form and the width of
output and input elements. Different from member functions, manipulators are
part of insertion or extraction statements.
9.3.6.1: The (v)form() and (v)scan() members
To format information to be inserted into a stream the member form()
is
available:
ostream& form(const char *format ...);
ostream
object. Therefore, it can be used in combination with, e.g., the
insertion operator:
cout.form("Hello %s", "world") << endl;
The memberfunction form()
is the analogue of C's fprintf()
function. When variadic functions are constructed in which information must be
inserted into a stream, the memberfunction vform()
can be used, being the
analogue of vfprintf()
.
To scan information from a stream, the memberfunction scan()
can be
used, which is the analogue of C's fscanf()
function. Similarly to
vfscanf()
, the memberfunction vscan()
can be used in variadic
functions.
9.3.6.2: Format states: dec, hex, oct manipulators
The iostream
objects maintain format states controlling the default
formatting of values. The format states can be controlled by memberfunctions
and by manipulators. Manipulators are inserted into the stream, the
memberfunctions are used by themselves.
The manipulators are dec, hex
and oct
, enforcing the display of
integral numbers in, respectively, decimal, hexadecimal and octal format.
The default conversion is decimal. The
conversion takes effect on information inserted into the stream after
processing the manipulators. So, a statement like:
cout << 16 << ", " << hex << 16 << ", " << oct << 16;
16, 10, 20
9.3.6.3: Setting the precision: the member precision()
The function precision()
is used to define the precision of the display of
floating point numbers. The function expects the number of digits (not
counting the decimal point or the minus sign) that are to be displayed as its
argument. For example,
cout.precision(4); cout << sqrt(2) << endl; cout.precision(6); cout << -sqrt(2) << endl;results in the following output:
1.414 -1.41421
when used without argument, precision()
returns the actual precision
value:
cout.precision(4); cout << cout.precision() << ", " << sqrt(2) << endl;
Note: precision()
is not a manipulator, but a memberfunction. Therefore,
cout.precision()
rather than precision()
is inserted into the stream.
9.3.6.4: Setting the display form: the member setf()
The member-function setf()
is used to define the way numbers are
displayed. It expects one or two arguments, all flags of the iostream
class. In the following examples, cout
is used, but other ostream
objects might have been used as well:
cout.setf(ios::showbase)
0x
for hexadecimal
values, 0
for octal values. For example:
cout.setf(ios::showbase); cout << 16 << ", " << hex << 16 << ", " << oct << 16 << endl;results in:
16, 0x10, 020
cout.setf(ios::showpoint)
cout.setf(ios::showpoint); cout << 16.0 << ", " << 16.1 << ", " << 16 << endl;results in:
16.0000, 16.1000, 16
16
is an integral rather than a real number, and is not
given a decimal point.
If ios::showpoint
is not used, then trailing zeros are discarded. If the
decimal part is zero, then the decimal point is discarded as well.
dec, hex
and oct
manipulators
cout.setf(ios::dec, ios::basefield); cout.setf(ios::hex, ios::basefield);or
cout.setf(ios::oct, ios::basefield);can be used.
cout.setf(ios::fixed,
ios::floatfield)
or cout.setf(ios::scientific, ios::floatfield)
can be
used. These settings result in, respectively, a fixed value display or a
scientific (power of 10) display of numbers. For example,
cout.setf(ios::fixed, ios::floatfield); cout << sqrt(200) << endl; cout.setf(ios::scientific, ios::floatfield); cout << sqrt(200) << endl;results in
14.142136 1.414214e+01
As a summary:
setf(ios::showbase)
is used to display the numeric base of integral
values,
setf(ios::showpoint)
is used to display the trailing decimal point
and trailing zeros of real numbers
setf(ios::dec, ios::basefield), setf(ios::hex, ios::basefield)
and
setf(ios::oct, ios::basefield)
can be used instead of the dec, hex
and
oct
manipulators.
cout.setf(ios::scientific, ios::floatfield)
and
cout.setf(ios::fixed, ios::floatfield)
can be used to obtain a fixed or
scientific (power of 10) display of real values.
9.3.6.5: The manipulator setw()
The setw()
manipulator expects one argument: the width of the field that's
inserted or extracted next. It can be used as manipulator for insertion, where
it defines the maximum number of characters that are displayed for the field,
and it can be used with extraction, where it defines the maximum number of
characters that are inserted into an array.
For example, to insert 20 characters into cout
, use:
cout << setw(20) << 8 << endl;
To prevent array-bounds overflow when extracting from cin
, setw()
can
be used as well:
cin >> setw(sizeof(array)) >> array;
cin
is split into
substrings of at most sizeof(array) - 1
characters, and an ascii-z is
appended.
Notes:
setw()
is valid only for the next field. It does not act like
e.g., hex
which changes the general state of the output stream for
displaying numbers.
setw(sizeof(someArray))
is used, make sure that
someArray
really is an array, and not a pointer to an array: the size of a
pointer, being 2 or 4 bytes, is usually not the size of the array that it
points to....
setw()
the header-file iomanip.h
must be
included.
Strings can be processed similarly to iostream
objects, if objects of the
class istrstream
or ostrstream
are constructed. Objects of these
classes read information from memory and write information to memory,
respectively. These objects are created by constructors expecting the address
of a block of memory (and its size) as its argument. For example to write
something into a block of memory using a ostrstream
object, the following
code could be used:
char buffer[100]; ostrstream os(buffer, 100); // construct the ostrstream object // fill 'buffer' with a well-known text os << "Hello world " << endl << '\0'; cout << os.str(); // display the stringNote the final
'\0'
character (the ascii-z) that is appended:
ostrstream
objects do their own bookkeepping, and also accept non ascii-z
terminated information. Therefore, an ascii-z character must be appended to
the string when it is to be inserted into an ostream.
Note also the use of the memberfunction str()
, returning the string the
ostrstream
object operates on. Using str()
the existence of buffer
can be hidden from the users of the ostrstream
object.
The following memberfunctions are available for strstream
objects:
istrstream::istrstream(const char *str [, int size])
: This
constructor creates an input string class istrstream
object, associating
it with an existing buffer starting at str
, of size size
.
If size
is not specified, the buffer is treated as a null-terminated
string.
ostrstream::ostrstream()
: This constructor creates a new stream for
output to a dynamically managed string, which will grow as needed.
ostrstream::ostrstream(char *str, int size [, int mode])
: This
constructor creates a new stream for output to a statically defined string of
length size
, starting at str
. The mode
parameter may
optionally be specified as one of the iostream modes. By default ios::out
is used.
int ostrstream::pcount()
: returns the current length of the string
associated with this ostrstream
object.
char *ostrstream::str()
: The memberfunction returns a pointer to the
string managed by this ostrstream
object. This function implies
freeze()
, see below:
void ostrstream::freeze ([int n])
: If n
is nonzero (the default),
the string associated with this ostrstream
object must not change
dynamically anymore. While frozen, it will not be reallocated if it needs
more space, and it will not be deallocated when the ostrstream
object is
destroyed. freeze(1)
can be used to refer to the string as a pointer
after creating it via ostrstream
facilities.
int ostrstream::frozen()
: This member can be used to
test whether freeze(1)
is in effect for this string.
In order to use the strstream
classes, the header file strstream.h
header-file must be included.