Module 11 The C++ I/O System
The most fundamental point to understand about the C++ I/O system is that it operates on streams. A stream is an abstraction that either produces or consumes information. A stream is linked to a physical device by the C++ I/O system. All streams behave in the same manner, even if the actual physical devices they are linked to differ. Because all streams act the same, the same I/O functions and operators can operate on virtually any type of device. For example, the same method that you use to write to the screen can be used to write to a disk...
Module 11
The C++ I/O System
Table of Contents
CRITICAL SKILL 11.1: Understand I/O streams .............................................................................................. 2
CRITICAL SKILL 11.2: Know the I/O class hierarchy ....................................................................................... 3
CRITICAL SKILL 11.3: Overload the > operators .............................................................................. 4
CRITICAL SKILL 11.4: Format I/O by using iso member functions ............................................................... 10
CRITICAL SKILL 11.5: Format I/O by using manipulators............................................................................. 16
CRITICAL SKILL 11.6: Create your own manupulators ................................................................................ 18
CRITICAL SKILL 11.7: Open and close files................................................................................................... 20
CRITICAL SKILL 11.8: Read and write text files ............................................................................................ 23
CRITICAL SKILL 11.9: Read and write binary files ........................................................................................ 25
CRITICAL SKILL 11.10: Know additional file functions ................................................................................. 29
CRITICAL SKILL 11.11: Use randon access files I/O ..................................................................................... 35
CRITICAL SKILL 11.12: Check I/O system status .......................................................................................... 37
Since the beginning of this book you have been using the C++ I/O system, but you have been doing so
without much formal explanation. Since the I/O system is based upon a hierarchy of classes, it was not
possible to present its theory and details without first discussing classes and inheritance. Now it is time
to examine the C++ I/O system in detail. The C++ I/O system is quite large, and it won’t be possible to
discuss here every class, function, or feature, but this module will introduce you to the most important
and commonly used parts. Specifically, it shows how to overload the > operators so that you
can input or output objects of classes that you design. It describes how to format output and how to use
I/O manipulators. The module ends by discussing file I/O.
Old vs. Modern C++ I/O
There are currently two versions of the C++ object-oriented I/O library in use: the older one that is based
upon the original specifications for C++ and the newer one defined by Standard C++. The old I/O library
is supported by the header file . The new I/O library is supported by the header
1 C++ A Beginner’s Guide by Herbert Schildt
. For the most part, the two libraries appear the same to the programmer. This is because the
new I/O library is, in essence, simply an updated and improved version of the old one. In fact, the vast
majority of differences between the two occur beneath the surface, in the way that the libraries are
implemented—not in how they are used.
From the programmer’s perspective, there are two main differences between the old and new C++ I/O
libraries. First, the new I/O library contains a few additional features and defines some new data types.
Thus, the new I/O library is essentially a superset of the old one. Nearly all programs originally written
for the old library will compile without substantive changes when the new library is used. Second, the
old-style I/O library was in the global namespace. The new-style library is in the std namespace. (Recall
that the std namespace is used by all of the Standard C++ libraries.) Since the old-style I/O library is now
obsolete, this book describes only the new I/O library, but most of the information is applicable to the
old I/O library as well.
CRITICAL SKILL 11.1: C++ Streams
The most fundamental point to understand about the C++ I/O system is that it operates on streams. A
stream is an abstraction that either produces or consumes information. A stream is linked to a physical
device by the C++ I/O system. All streams behave in the same manner, even if the actual physical devices
they are linked to differ. Because all streams act the same, the same I/O functions and operators can
operate on virtually any type of device. For example, the same method that you use to write to the
screen can be used to write to a disk or to the printer.
In its most common form, a stream is a logical interface to a file. As C++ defines the term “file,” it can
refer to a disk file, the screen, the keyboard, a port, a file on tape, and so on. Although files differ in
form and capabilities, all streams are the same. The advantage to this approach is that to you, the
programmer, one hardware device will look much like any other. The stream provides a consistent
interface.
A stream is linked to a file through an open operation. A stream is disassociated from a file through a
close operation.
There are two types of streams: text and binary. A text stream is used with characters. When a text
stream is being used, some character translations may take place. For example, when the newline
character is output, it may be converted into a carriage return–linefeed sequence. For this reason, there
might not be a one-to-one correspondence between what is sent to the stream and what is written to
the file. A binary stream can be used with any type of data. No character translations will occur, and
there is a one-to-one correspondence between what is sent to the stream and what is actually
contained in the file.
One more concept to understand is that of the current location. The current location (also referred to as
the current position) is the location in a file where the next file access will occur. For example, if a file is
100 bytes long and half the file has been read, the next read operation will occur at byte 50, which is the
current location.
2 C++ A Beginner’s Guide by Herbert Schildt
To summarize: In C++, I/O is performed through a logical interface called a stream. All streams have
similar properties, and every stream is operated upon by the same I/O functions, no matter what type of
file it is associated with. A file is the actual physical entity that contains
The C++ I/O System
the data. Even though files differ, streams do not. (Of course, some devices may not support all
operations, such as random-access operations, so their associated streams will not support these
operations either.)
The C++ Predefined Streams
C++ contains several predefined streams that are automatically opened when your C++ program begins
execution. They are cin, cout, cerr, and clog. As you know, cin is the stream associated with standard
input, and cout is the stream associated with standard output. The cerr stream is linked to standard
output, and so is clog. The difference between these two streams is that clog is buffered, but cerr is not.
This means that any output sent to cerr is immediately output, but output to clog is written only when a
buffer is full. Typically, cerr and clog are streams to which program debugging or error information is
written. C++ also opens wide (16-bit) character versions of the standard streams called wcin, wcout,
wcerr, and wclog. These streams exist to support languages, such as Chinese, that require large
character sets. We won’t be using them in this book. By default, the C++ standard streams are linked to
the console, but they can be redirected to other devices or files by your program. They can also be
redirected by the operating system.
CRITICAL SKILL 11.2: The C++ Stream Classes
As you learned in Module 1, C++ provides support for its I/O system in .Inthis header, a
rather complicated set of class hierarchies is defined that supports I/O operations. The I/O classes begin
with a system of template classes. As you will learn in Module 12, a template defines the form of a class
without fully specifying the data upon which it will operate. Once a template class has been defined,
specific instances of the template class can be created. As it relates to the I/O library, Standard C++
creates two specific versions of these template classes: one for 8-bit characters and another for wide
characters. These specific versions act like any other classes, and no familiarity with templates is
required to fully utilize the C++ I/O system.
The C++ I/O system is built upon two related, but different, template class hierarchies. The first is
derived from the low-level I/O class called basic_streambuf. This class supplies the basic, low-level input
and output operations, and provides the underlying support for the entire C++ I/O system. Unless you
are doing advanced I/O programming, you will not need to use basic_streambuf directly. The class
hierarchy that you will most commonly be working with is derived from basic_ios. This is a high-level I/O
class that provides formatting, error-checking, and status information related to stream I/O. (A base
class for basic_ios is called ios_base, which defines several traits used by basic_ios.) basic_ios is used as
a base for several derived classes, including basic_istream, basic_ostream, and basic_iostream. These
classes are used to create streams capable of input, output, and input/output, respectively.
3 C++ A Beginner’s Guide by Herbert Schildt
As explained, the I/O library creates two specific versions of the I/O class hierarchies: one for 8-bit
characters and one for wide characters. This book discusses only the 8-bit character classes since they
are by far the most frequently used. Here is a list of the mapping of template class names to their
character-based versions.
The character-based names will be used throughout the remainder of this book, since they are the
names that you will use in your programs. They are also the same names that were used by the old I/O
library. This is why the old and the new I/O library are compatible at the source code level.
One last point: The ios class contains many member functions and variables that control or monitor the
fundamental operation of a stream. It will be referred to frequently. Just remember that if you include
in your program, you will have access to this important class.
1. What is a stream? What is a file?
2. What stream is connected to standard output?
3. C++ I/O is supported by a sophisticated set of class hierarchies. True or false?
CRITICAL SKILL 11.3: Overloading the I/O Operators
In the preceding modules, when a program needed to output or input the data associated with a class,
member functions were created whose only purpose was to output or input the class’ data. While there
is nothing, in itself, wrong with this approach, C++ allows a much better way of performing I/O
operations on classes: by overloading the > I/O operators.
In the language of C++, the > operator is called the extraction operator because it extracts data from a
4 C++ A Beginner’s Guide by Herbert Schildt
stream. The operator functions that overload the insertion and extraction operators are generally called
inserters and extractors, respectively.
In , the insertion and extraction operators are overloaded for all of the C++ built-in types.
Here you will see how to define these operators relative to classes that you create.
Creating Inserters
As a simple first example, let’s create an inserter for the version of the ThreeD class shown here:
The C++ I/O System
To create an inserter function for an object of type ThreeD, overload the If you eliminate the code that is specific to the ThreeD class, you are left with the skeleton for an
inserter function, as shown here:
Of course, it is permissible for obj to be passed by reference.
Within wide boundaries, what an inserter function actually does is up to you. However, good
programming practice dictates that your inserter should produce reasonable output. Just make sure that
you return stream.
Using Friend Functions to Overload Inserters
6 C++ A Beginner’s Guide by Herbert Schildt
In the preceding program, the overloaded inserter function is not a member of ThreeD. In fact, neither
inserter nor extractor functions can be members of a class. The reason is that when an operator function
is a member of a class, the left operand (implicitly passed using the this pointer) is an object of that
class. There is no way to change this. However, when inserters are overloaded, the left operand is a
stream, and the right operand is an object of the class being output. Therefore, overloaded inserters
must be nonmember functions.
The fact that inserters must not be members of the class they are defined to operate on raises a serious
question: How can an overloaded inserter access the private elements of a class? In the preceding
program, the variables x, y,and z were made public so that the inserter could access them. But hiding
data is an important part of OOP, and forcing all data to be public is a serious inconsistency. However,
there is a solution: an inserter can be a friend of a class. As a friend of the class for which it is defined, it
has access to private data. Here, the ThreeD class and sample program are reworked, with the
overloaded inserter declared as a friend:
// Use a friend to overload Overloading Extractors
To overload an extractor, use the same general approach that you use when overloading an inserter. For
example, the following extractor inputs 3-D coordinates into an object of type ThreeD. Notice that it also
prompts the user.
An extractor must return a reference to an object of type istream. Also, the first parameter must be a
reference to an object of type istream. This is the stream that occurs on the left side of the >>. The
second parameter is a reference to the variable that will be receiving input. Because it is a reference, the
second parameter can be modified when information is input.
The skeleton of an extractor is shown here:
The following program demonstrates the extractor for objects of type ThreeD:
8 C++ A Beginner’s Guide by Herbert Schildt
A sample run is shown here:
Like inserters, extractor functions cannot be members of the class they are designed to operate upon.
They can be friends or simply independent functions.
9 C++ A Beginner’s Guide by Herbert Schildt
Except for the fact that you must return a reference to an object of type istream, you can do anything
you like inside an extractor function. However, for the sake of structure and clarity, it is best to use
extractors only for input operations.
1. What is an inserter?
adjustfield basefield boolalpha dec
fixed floatfield hex internal
left oct right scientific
showbase showpoint showpos skipws
unitbuf uppercase
2. What is an extractor?
3. Why are friend functions often used for inserter or extractor functions?
Formatted I/O
Up to this point, the format for inputting or outputting information has been left to the defaults
provided by the C++ I/O system. However, you can precisely control the format of your data in either of
two ways. The first uses member functions of the ios class. The second uses a
special type of function called a manipulator. We will begin by looking at formatting using the ios
member functions.
CRITICAL SKILL 11.4: Formatting with the ios Member
Functions
Each stream has associated with it a set of format flags that control the way information is formatted by
a stream. The ios class declares a bitmask enumeration called fmtflags in which the following values are
defined. (Technically, these values are defined within ios_base, which is a base class for ios.)
10 C++ A Beginner’s Guide by Herbert Schildt
These values are used to set or clear the format flags. Some older compilers may not define the fmtflags
enumeration type. In this case, the format flags will be encoded into a long integer.
When the skipws flag is set, leading whitespace characters (spaces, tabs, and newlines) are discarded
when performing input on a stream. When skipws is cleared, whitespace characters are not discarded.
When the left flag is set, output is left-justified. When right is set, output is right-justified.
When the internal flag is set, a numeric value is padded to fill a field by inserting spaces between any
sign or base character. If none of these flags is set, output is right-justified by default.
By default, numeric values are output in decimal. However, it is possible to change the number base.
Setting the oct flag causes output to be displayed in octal. Setting the hex flag causes output to be
displayed in hexadecimal. To return output to decimal, set the dec flag.
Setting showbase causes the base of numeric values to be shown. For example, if the conversion base is
hexadecimal, the value 1F will be displayed as 0x1F.
By default, when scientific notation is displayed, the e is in lowercase. Also, when a hexadecimal value is
displayed, the x is in lowercase. When uppercase is set, these characters are displayed in uppercase.
Setting showpos causes a leading plus sign to be displayed before positive values. Setting showpoint
causes a decimal point and trailing zeros to be displayed for all floating-point output—whether needed
or not.
By setting the scientific flag, floating-point numeric values are displayed using scientific notation. When
fixed is set, floating-point values are displayed using normal notation. When neither flag is set, the
compiler chooses an appropriate method.
When unitbuf is set, the buffer is flushed after each insertion operation. When boolalpha is set,
Booleans can be input or output using the keywords true and false.
Since it is common to refer to the oct, dec, and hex fields, they can be collectively referred to as
basefield. Similarly, the left, right, and internal fields can be referred to as adjustfield.
Finally, the scientific and fixed fields can be referenced as floatfield.
Setting and Clearing Format Flags
To set a flag, use the setf( ) function. This function is a member of ios. Its most common form is shown
here:
fmtflags setf(fmtflags flags);
This function returns the previous settings of the format flags and turns on those flags specified by flags.
For example, to turn on the showbase flag, you can use this statement:
11 C++ A Beginner’s Guide by Herbert Schildt
stream.setf(ios::showbase);
Here, stream is the stream you want to affect. Notice the use of ios:: to qualify showbase. Because
showbase is an enumerated constant defined by the ios class, it must be qualified by ios when it is
referred to. This principle applies to all of the format flags.
The following program uses setf( ) to turn on both the showpos and scientific flags:
The output produced by this program is shown here:
+123 +1.232300e+002
You can OR together as many flags as you like in a single call. For example, by ORing together scientific
and showpos, as shown next, you can change the program so that only one call is made to setf( ):
cout.setf(ios::scientific | ios::showpos);
To turn off a flag, use the unsetf( ) function, whose prototype is shown here: void unsetf(fmtflags flags);
The flags specified by flags are cleared. (All other flags are unaffected.)
Sometimes it is useful to know the current flag settings. You can retrieve the current flag values using
the flags( ) function, whose prototype is shown here: fmtflags flags( );
This function returns the current value of the flags relative to the invoking stream. The following form of
flags( ) sets the flag values to those specified by flags and returns the previous flag values: fmtflags
flags(fmtflags flags); The following program demonstrates flags( ) and unsetf( ):
12 C++ A Beginner’s Guide by Herbert Schildt
The program produces this output:
showpos is cleared for cout.
Setting showpos for cout.
showpos is set for cout.
Clearing showpos for cout.
showpos is cleared for cout.
In the program, notice that the type fmtflags is preceded by ios:: when f is declared. This is necessary
since fmtflags is a type defined by ios. In general, whenever you use the name of a type or enumerated
constant that is defined by a class, you must qualify it with the name of the class.
Setting the Field Width, Precision, and Fill Character
13 C++ A Beginner’s Guide by Herbert Schildt
In addition to the formatting flags, there are three member functions defined by ios that set these
additional format values: the field width, the precision, and the fill character. The functions that set
these values are width( ), precision( ), and fill( ), respectively. Each is examined in turn.
By default, when a value is output, it occupies only as much space as the number of characters it takes
to display it. However, you can specify a minimum field width by using the width( ) function. Its
prototype is shown here:
streamsize width(streamsize w);
Here, w becomes the field width, and the previous field width is returned. In some implementations, the
field width must be set before each output. If it isn’t, the default field width is used. The streamsize type
is defined as some form of integer by the compiler.
After you set a minimum field width, when a value uses less than the specified width, the field will be
padded with the current fill character (space, by default) to reach the field width. If the size of the value
exceeds the minimum field width, then the field will be overrun. No values are truncated.
When outputting floating-point values in scientific notation, you can determine the number of digits to
be displayed after the decimal point by using the precision( ) function. Its prototype is shown here:
streamsize precision(streamsize p);
Here, the precision is set to p, and the old value is returned. The default precision is 6. In some
implementations, the precision must be set before each floating-point output. If you don’t set it, the
default precision is used.
By default, when a field needs to be filled, it is filled with spaces. You can specify the fill character by
using the fill( ) function. Its prototype is
char fill(char ch);
After a call to fill( ), ch becomes the new fill character, and the old one is returned.
Here is a program that demonstrates these three functions:
14 C++ A Beginner’s Guide by Herbert Schildt
As mentioned, in some implementations, it is necessary to reset the field width before each output
operation. This is why width( ) is called repeatedly in the preceding program. There are overloaded
forms of width( ), precision( ), and fill( ) that obtain, but do not change, the current setting. These forms
are shown here:
char fill( ); streamsize width( ); streamsize precision( );
1. What does boolalpha do?
2. What does setf( ) do?
3. What function is used to set the fill character?
15 C++ A Beginner’s Guide by Herbert Schildt
CRITICAL SKILL 10.5: Using I/O Manipulators
The C++ I/O system includes a second way in which you can alter the format parameters of a stream.
This method uses special functions, called manipulators, that can be included in an I/O expression. The
standard manipulators are shown in Table 11-1. To use those manipulators that take arguments, you
must include in your program.
Manipulator Purpose Input/Output
boolalpha Turns on boolalpha flag Input/Output
dec Turns on dec flag Input/Output
Outputs a newline character and flushes the
endl Output
stream
ends Outputs a null Output
fixed Turns on fixed flag Output
flush Flushes a stream Output
hex Turns on hex flag Input/Output
internal Turns on internal flag Output
left Turns on left flag Output
noboolalpha Turns off boolalpha flag Input/Output
noshowbase Turns off showbase flag Output
noshowpoint Turns off showpoint flag Output
noshowpos Turns off showpos flag Output
noskipws Turns off skipws flag Input
nounitbuf Turns off unitbuf flag Output
nouppercase Turns off uppercase flag Output
oct Turns on oct flag Input/Output
resetiosflags (fmtflags f) Turns off the flags specified in f Input/Output
right Turns on right flag Output
scientific Turns on scientific flag Output
setbase(int base) Sets the number base to base Input/Output
setfill(int ch) Sets the fill character to ch Output
setiosflags(fmtflags f) Turns on the flags specified in f Input/Output
setprecision (int p) Sets the number of digits of precision Output
setw(int w) Sets the field width to w Output
showbase Turns on showbase flag Output
showpoint Turns on showpoint flag Output
Table 11-1 The C++ I/O Manipulators
16 C++ A Beginner’s Guide by Herbert Schildt
A
Manipulator Purpose Input/Output
showpos Turns on showpos flag Output
skipws Turns on skipws flag Input
unitbuf Turns on unitbuf flag Output
uppercase Turns on uppercase flag Output
ws Skips leading whitespace Input
Table 11-1 The C++ I/O Manipulators (continued)
manipulator is used as part of a larger I/O expression. Here is a sample program that uses manipulators
to control the format of its output:
Notice how the manipulators occur in the chain of I/O operations. Also, notice that when a manipulator
does not take an argument, such as endl in the example, it is not followed by parentheses.
The following program uses setiosflags( ) to set the scientific and showpos flags:
17 C++ A Beginner’s Guide by Herbert Schildt
The program shown next uses ws to skip any leading whitespace when inputting a string into s:
CRITICAL SKILL 11.6: Creating Your Own Manipulator Functions
You can create your own manipulator functions. There are two types of manipulator functions: those
that take arguments and those that don’t. The creation of parameterized manipulators requires the use
of techniques beyond the scope of this book. However, the creation of parameterless manipulators is
quite easy and is described here.
All parameterless manipulator output functions have this skeleton:
Here, manip_name is the name of the manipulator. It is important to understand that even though the
manipulator has as its single argument a pointer to the stream upon which
it is operating, no argument is specified when the manipulator is used in an output expression.
The following program creates a manipulator called setup( ) that turns on left justification, sets the field
width to 10, and specifies that the dollar sign will be the fill character.
18 C++ A Beginner’s Guide by Herbert Schildt
Custom manipulators are useful for two reasons. First, you might need to perform an I/O operation on a
device for which none of the predefined manipulators applies—a plotter, for example. In this case,
creating your own manipulators will make it more convenient when outputting to the device. Second,
you may find that you are repeating the same sequence of operations many times. You can consolidate
these operations into a single manipulator, as the foregoing program illustrates.
All parameterless input manipulator functions have this skeleton:
For example, the following program creates the prompt( ) manipulator. It displays a prompting message
and then configures input to accept hexadecimal.
19 C++ A Beginner’s Guide by Herbert Schildt
Remember that it is crucial that your manipulator return stream. If this is not done, then your
manipulator cannot be used in a chain of input or output operations.
1. What does endl do?
2. What does ws do?
3. Is an I/O manipulator used as part of a larger I/O expression?
File I/O
You can use the C++ I/O system to perform file I/O. To perform file I/O, you must include the header
in your program. It defines several important classes and values.
CRITICAL SKILL 11.7: Opening and Closing a File
In C++, a file is opened by linking it to a stream. As you know, there are three types of streams: input,
output, and input/output. To open an input stream, you must declare the stream to be of class ifstream.
20 C++ A Beginner’s Guide by Herbert Schildt