11. Customizing Input and Output
"Keep it simple: as simple as possible, but no simpler." -- Albert Einstein
11.1 Regularity and irregularity
The iostream library which is the input/output part of the ISO C++ standard library. This library provides a unified and extensible framework for I/O of text. In this chapter we will learn number of ways in which we can tailor I/O to our needs
As programmers we naturally prefer regularity: handle everything in a uniform fashion! It is simpler and pleases our engineering sense. But our users often have strong preferences for very irregular arrangements of their data. We have to balance these two factors.
11.2 Output formatting
People care a lot about the minor details of output. Our aim as a programmer is to keep output as clear and close to user's expectation as possible.
11.2.1 Integer output
Integer values can be output as octal, decimal, and hexadecimal. Most output uses decimal. Below code represents output 1234 (decimal) to decimal, hexadecimal, and octal:
cout << 1234 << "\t(decimal)\n"
<< hex << 1234 << "\t(hexadecimal)\n"
<< oct << 1234 << "\t(octal)\n";
Output for this is:
1234 (decimal)
4d2 (hexadecimal)
2322 (octal)
11.2.2 Integer input
By default, >> assumes that numbers use the decimal notation, but it can also read hexadecimal or octal integers:
int a;
int b;
int c;
int d;
cin >> a >> hex >> b >> oct >> c >> d;
cout << a << '\t' << b << '\t' << c << '\t' << d << '\n';
If you type in:
1234 4d2 2322 2322
this will print:
1234 1234 1234 1234
11.2.3 Floating-point output
This is very important in case you are dealing with
scientific computation. These are handled using
iostream
manipulator in a manner very
similar to that of integer values:
cout << 1234.56789 << "\t\t(defaultfloat)\n" // \t\t to line up columns
<< fixed << 1234.56789 << "\t(fixed)\n"
<< scientific << 1234.56789 << "\t(scientific)\n";
This prints:
1234.57 (general)
1234.567890 (fixed)
1.234568e+003 (scientific)
11.2.4 Precision
By default the precision for floating point numbers is
six total digits using the defaultfloat
format. The chooses the most appropriate format and the
number is rounded to give the best approximation that
can be printed using only six digits.
A programmer can set the precision using the manipulator setprecision(). For example:
cout << 1234.56789 << '\t'
<< fixed << 1234.56789 << '\t'
<< scientific << 1234.56789 << '\n';
cout << defaultfloat << setprecision(5)
<< 1234.56789 << '\t'
<< fixed << 1234.56789 << '\t'
<< scientific << 1234.56789 << '\n';
cout << defaultfloat << setprecision(8)
<< 1234.56789 << '\t'
<< fixed << 1234.56789 << '\t'
<< scientific << 1234.56789 << '\n';
This prints (note the rounding):
1234.57 1234.567890 1.234568e+003
1234.6 1234.56789 1.23457e+003
1234.5679 1234.56789000 1.23456789e+003
11.2.5 Fields
Using scientific and fixed formats, a programmer can
control exactly how much space a value takes up on output.
That’s clearly useful for printing tables, etc.
“set field width” manipulator setw()
can be used to specify exactly how many character
positions an integer value or string value will occupy.
Example:
cout << 123456
<<'|'<< setw(4) << 123456 << '|'
<< setw(8) << 123456 << '|'
<< 123456 << "|\n";
This prints:
123456|123456| 123456|123456|
Drill
We'd probably like our calculator to give high precision answers: use set precision to up the number of output digits.
11.3 File opening and positioning
In C++, a file is is an abstraction of what the operating system
provides. A file is sequence of bytes numbered
from 0 upwards. The question is how to access these bytes.
The simplest example of this is that if we open an
istream
for a file, we can read from the
file, whereas if we open a file with an ostream
,
we can write to it.
11.3.1 File open modes
A file can be opened in several modes. By default,
ifstream
opens it for reading and
ofstream
opens for writing. These take care
of the most common needs but there are several other
alternatives given in the book. A file mode is
optionally specified after the name of the file. For example:
ofstream of1 {name1}; //defaults to ios_base::out
ifstream if1 {name2}; //defaults to ios_base::in
ofstream ofs {name, ios_base::app}; // app means "append"
fstream fs {"myfile",ios_base::in|ios_base::out};//both in and out
11.3.2 Binary files
We can tell our IO streams to output binary data,
i.e., represent an int not as a sequence of chars
but as 0100100 etc. by using
ios_base::binary
.
11.3.3 Positioning in files
We can also go to a specific position in a file for I/O. Advanced stuff we won't do, but it is good to know we can! (We might use this if we were writing a new database, for instance, or editing a movie.)
11.4 String streams
A string
can be used as a source of an
istream
or the target for an
ostream
.
An istream
that reads from a
string
is called
an istringstream
and an ostream
that stores characters written to it in a
string
is called an ostringstream
.
We can use an istringstream
for extracting
numeric values from a string
:
double str_to_double(string s)
// if possible, convert characters in s to floating-point value
{
istringstream is {s}; // make a stream so that we can read from s
double d;
is >> d;
if (!is) error("double format error: ",s);
return d;
}
double d1 = str_to_double("12.4");
double d2 = str_to_double("1.34e–3");
double d3 = str_to_double("twelve point three");
11.5 Line-oriented input
If we want to read everything on a line at once and want to
decide the format later. This can be done using the
function getline()
. For example:
string name;
getline(cin,name); // input: Dennis Ritchie
cout << name << '\n'; // output: Dennis Ritchie
>>
reads only until it encounters whitespace. If we
use >>
instead of getline()
the output
will be 'Dennis'.
11.6 Character classification
Sometimes we go down to a level of abstraction and read
individual characters. This is certainly more work but when
we do this, we have a full control over what we are
doing. Consider tokenizing this expression:
1+4*x<=y/z*5
to be separated
into the eleven tokens.
We could use >>
to read numbers. but
trying to read identifiers as strings would cause
x<=y
to to be read as one string. Similarly
it will read z*
as one string. So, instead we
can write something like this:
for (char ch; cin.get(ch); ) {
if (isspace(ch)) { // if ch is whitespace
// do nothing (i.e., skip whitespace)
}
if (isdigit(ch)) {
// read a number
}
else if (isalpha(ch)) {
// read an identifier
}
else {
// deal with operators
}
}
11.7 Using nonstandard separators
We can define our own stream type to recognize different separators than the default, e.g., to recognize something other than whitespace as separating strings. (Think of trying to extract words from a stream of text: we don't want "To be or not to be, that is the question" to return "be," as a word!) This is difficult programming, and beyond what we can tackle in this course, but the example from the book is worth studying.)
11.8 And there is so much more
The details of I/O seem infinite. They probably are, since they are limited only by human inventiveness and capriciousness. For example, we have not considered the complexity implied by natural languages.
Test Yourself!

- Which of the following is an integer output manipulator?
- all mentioned
- showbase
- hex
- noshowbase
- Which of the following is not a basic floating-point output manipulator?
- defaultfloat
- showbase
- scientific
- fixed
- What does isalnum(c) do?
- is c a letter or a decimal digit?
- is c printable?
- is c a decimal digit?
- is c whitespace?
Answers
1. a; 2. b; 3. a;
Drill
