Part B - Foundations
Input and Output Examples
Describe the member functions that control standard input and output
Introduce manipulators to format data on input and output objects
Describe the member functions that manage the state of input and output objects
"Designing and implementing a general input/output facility for
a programming language is notoriously difficult. ... Nobody
has come up with a solution that pleases everyone" (Stroustrup, 1997) Streams
| Output
| Input
| State
| Robust Validation
| Summary
| Exercises
The compound types that define the structure of the input and output objects introduced
in the first chapter include public member functions for controlling input
and output. The compound types and their objects belong to a library module named
iostream and are defined in the <iostream>
header file. These public member functions report the state of each objects and provide
control for formatting the data passing through these objects.
This chapter describes the input and output objects, along with
the member functions that control state and formatting. This
description demonstrates the role that member functions play and
sets the stage for subsequent coding of member functions on our own
compound types.
Streams
Input and output objects operate on streams of data. Streams
are sequences involving indeterminate numbers of characters.
The input and output objects of the iostream library
manage the flow of characters respectively from input streams to memory and from
memory to output streams. These objects represent physical devices, such
as keyboards and displays. The input object extracts data from the input stream
attached to it. The output object inserts data into the output stream attached
to it.

The input object converts a sequence of characters from a stream
into values stored in system memory. The output object
converts values stored in system memory into a sequence
of characters in a stream. Both objects are aware
of the data type associated with the region of memory accessed
and make appropriate conversions based upon that type.

Note that the data stored in regions of memory have types,
while the data in the streams themselves are not associated with any type.
Output Objects
An output object is an instance of the ostream
type that represents an output device. An ostream object copies data from system memory into
an output stream; in copying, it converts the data in system memory
into a sequence of characters.
The iostream module defines three standard output
objects:
- cout - sends a buffered sequence
of characters to the standard output device
- cerr - sends an unbuffered
sequence of characters to the standard error output device
- clog - sends a buffered sequence
of characters to the standard error output device
Insertion
The expression for inserting data into an output stream takes
the form
outputObject << identifier
where outputObject is the
name of the ostream object. << is the insertion operator.
identifier is the name of the variable
or the object that stores the data.
For example,
int i = 6;
char c = ' ';
double x = 9.75;
char s[] = "Harry";
cout << i;
cout << c;
cout << x;
cout << c;
cout << s;
cout << endl;
cerr << "Data has been written";
|
6 9.75 Harry
Data has been written
|
Each expression with an ostream object
as its left operand converts the data in its right operand into a sequence
of characters according to the right operand's type.
endl inserts a newline character
into the stream and flushes the stream's buffer.
Cascaded Insertion
We may combine these expressions into a single statement that specifies
multiple insertions:
int i = 6;
char c = ' ';
double x = 9.75;
char s[] = "Harry";
cout << i << c << x << c << s << endl;
cerr << "Data has been written";
|
6 9.75 Harry
Data has been written
|
We call such repeated use of the insertion operator
cascading.
Member Functions
The ostream type exposes the
following member functions for formatting conversions:
- width(int) - sets the field width
to the integer received
- fill(char) - sets the padding
character to the character received
- setf(...) - sets a formatting
flag to the flag received
- unsetf(...) - unsets a
formatting flag for the flag received
- precision(int) - sets the
decimal precision to the integer received
width
The width() member function
specifies the minimum width of the next
output field:
// Field Width
// width.cpp
#include <iostream>
using namespace std;
int main() {
int attendance = 27;
cout << "1234567890" << endl;
cout.width(10);
cout << attendance << endl;
cout << attendance << endl;
}
|
1234567890
27
27
|
width(int) applies
only to the next field. Note how the field width for
the first output of attendance is 10,
while the field width for the second output of attendance is just the minimum number
of characters needed to display the value (2).
fill
The fill() member function defines the padding
character. This is the
character inserted into the stream wherever text occupies less
space than the field width. The default fill character is
' ' (space). To pad a field
with '*''s, we add:
// Padding
// fill.cpp
#include <iostream>
using namespace std;
int main() {
int attendance = 27;
cout << "1234567890" << endl;
cout.fill('*');
cout.width(10);
cout << attendance << endl;
}
|
1234567890
********27
|
The padding character remains unchanged, until we reset it.
setf, unsetf
The setf() and unsetf() member functions control alignment and formatting.
The control flags include:
Control Flag |
Result |
ios::left |
align left |
ios::right |
align right |
ios::scientific |
d.ddddddEdd |
ios::fixed |
ddd.ddd |
Note the scope resolution (ios::)
on these flags.
The default format is general format, which outputs data
in the simplest way possible. To output a fixed number of
decimal places, we use fixed format. To turn on
fixed format, we pass ios::fixed to
setf():
// Fixed Format
// fixed.cpp
#include <iostream>
using namespace std;
int main() {
double pi = 3.141592653;
cout << "1234567890" << endl;
cout.width(10);
cout.setf(ios::fixed);
cout << pi << endl;
}
|
1234567890
3.141593
|
Format settings persist until we change them.
To unset fixed format, we pass the ios::fixed flag to
the unsetf() member function:
// Unset Fixed Format
// unsetf.cpp
#include <iostream>
using namespace std;
int main() {
double pi = 3.141592653;
cout << "1234567890" << endl;
cout.width(10);
cout.setf(ios::fixed);
cout << pi << endl;
cout.unsetf(ios::fixed);
cout << pi << endl;
}
|
1234567890
3.141593
3.14159
|
To turn on scientific format, we pass the ios::scientific flag to the setf() member function:
// Scientific Format
// scientific.cpp
#include <iostream>
using namespace std;
int main() {
double pi = 3.141592653;
cout << "12345678901234" << endl;
cout.width(14);
cout.setf(ios::scientific);
cout << pi << endl;
}
|
12345678901234
3.141593e+00
|
To turn off scientific format, we pass the ios::scientific flag to
the unsetf() member function.
To turn on left-justification, we pass the ios::left flag to the setf() member function:
// Left Justified
// left.cpp
#include <iostream>
using namespace std;
int main() {
double pi = 3.141592653;
cout << "1234567890" << endl;
cout.width(10);
cout.fill('?');
cout.setf(ios::left);
cout << pi << endl;
}
|
1234567890
3.14159???
|
To turn off left-justification, we pass the ios::left
flag to the unsetf() member function:
The default alignment is right-justified.
precision
The precision() member function sets the precision of
the subsequent floating-point fields. The default
precision is 6 units. General,
fixed, and scientific formats count precision differently.
General format counts the number of significant digits.
Scientific and fixed formats
count the number of digits following the
decimal point. For a precision of 2 under general format, we write
// Precision
// precison.cpp
#include <iostream>
using namespace std;
int main() {
double pi = 3.141592653;
cout << "1234567890" << endl;
cout.width(10);
cout.precision(2);
cout << pi << endl;
}
|
1234567890
3.1
|
The precision setting applies to the output of all subsequent
floating-point values until we change it.
Manipulators
Manipulators provide an elegant alternative for modifying the format
setting. We use manipulators as operands to insertion operators.
Manipulators that take no arguments do not include parentheses.
Manipulators that don't take arguments are
defined in <iostream>.
Those that take arguments are defined in <iomanip>. We include <iomanip> whenever we use manipulators that
take arguments.
The insertion manipulators include:
Manipulator |
Effect |
fixed |
output floating-point numbers in fixed-point
format |
scientific |
output floating-point numbers in scientific
format |
left |
left justify |
right |
right justify |
flush |
flush the output buffer |
endl |
output end of line and flush the buffer |
setprecision(int) |
set the precision of the output |
setfill(int) |
set the fill character for the field width |
setbase(int) |
set the base of the number system for int output |
setw(int) |
set the field width for the next output operand
only |
Manipulators (except for setw(i)
which only modifies settings temporarily) modify the format settings
used by the insertion operator until we change them.
For example,
cout << fixed << left << setw(5) <<
setprecision(1) << 12.376 <<
setprecision(5) << 12.376 <<
endl;
|
12.4 12.37600
|
Reference Example
The following program produces the output listed on the right
#include <iostream>
#include <iomanip>
using namespace std;
int main( ) {
/* integers */
cout << "\n* ints *\n"
<< "1234567890\n"
<< "----------\n"
<< 4321 << '\n'
<< setw(7) << 4321 << '\n'
<< setw(7) << setfill('0') << 4321 << setfill(' ')<<'\n'
<< setw(7) << left << 4321 << right << '\n';
/* floats */
cout << "\n* floats *\n"
<< "1234567890\n"
<< "----------\n"
<< 4321.9876546F << '\n';
/* doubles */
cout << "\n* doubles *\n"
<< "1234567890\n"
<< "----------\n"
<< fixed << 4.9876546 << '\n'
<< setw(7) << setprecision(3) << 4.9876546 << '\n'
<< setw(7) << setfill('0') << 4.9876546 << '\n'
<< setw(7) << left << 4.9876546 << right << '\n';
/* characters */
cout << "\n* chars *\n"
<< "1234567890\n"
<< "----------\n"
<< 'd' << '\n'
<< int('d') << '\n';
}
|
* ints *
1234567890
----------
4321
4321
0004321
4321
* floats *
1234567890
----------
4321.99
* doubles *
1234567890
----------
4.987655
4.988
004.988
4.98800
* chars *
1234567890
----------
d
100
|
Note that
- a double or a float rounds to the requested precision
- char data displays in either
character or decimal format: to output its numeric code,
we cast the value to an
int (the value output for 'd'
here is its ASCII value).
Input Object
The input object is an instance of the istream
type that represents an input device. The object
extracts data from the input stream and stores it in
system memory: it converts the sequence of characters in the stream
into equivalent values in system memory.
Extraction
The expression for extracting characters from an input stream
takes the form
inputObject >> identifier
where inputObject is the name of
the input object. >> is the
extraction operator. identifier
is the name of the destination variable.
The iostream
library defines one standard input object for buffered input named
cin.
For example,
int i;
char c;
double x;
char s[8];
cout << "Enter an integer,\n"
"a character,\n"
"a floating-point number and\n"
"a string : " << flush;
cin >> i;
cin >> c;
cin >> x;
cin >> s; // possible overflow
cout << "Entered " << i << ' '
<< c << ' ' << x << ' ' << s << endl;
|
Enter an integer,
a character,
a floating-point and
a string : 6 - 9.75 Harry
Entered 6 - 9.75 Harry
|
Each expression with an istream object
as its left operand converts the next sequence of characters into the
data type of its right operand.
The cin object skips leading whitespace with
numeric, string and character types (in the same way that
scanf("%d"...), scanf("%lf"...), scanf("%s"...) and scanf("
%c"...) do in the C language).
// Leading Whitespace
// leading.cpp
#include <iostream>
using namespace std;
int main() {
char str[11];
cout << "Enter a string : " << endl;
cin >> str;
cout << "|" << str << "|" << endl;
}
|
Note: _ denotes space
Enter a string :
__abc
|abc|
|
Whitespace
cin treats whitespace in the input stream
as a delimiter for both numeric and string data types. For
C-style null-terminated string types, cin adds
the null byte after the last non-whitespace character stored in
memory:
// Trailing Whitespace
// trailing.cpp
#include <iostream>
using namespace std;
int main() {
char str[11];
cout << "Enter a string : " << endl;
cin >> str;
cout << "|" << str << "|" << endl;
}
|
Note: _ denotes space
Enter a string :
__abc__
|abc|
|
Cascaded Extraction
The extraction operator (>>),
like the insertion operator, accepts cascading:
int i;
char c;
double x;
char s[8];
cout << "Enter an integer,\n"
"a character,\n"
"a floating-point number and\n"
"a string : " << flush;
cin >> i >> c >> x >> s;
cout << "Entered " << i << ' '
<< c << ' ' << x << ' ' << s << endl;
|
Enter an integer,
a character,
a floating-point and
a string : 6 - 9.75 Harry
Entered 6 - 9.75 Harry
|
Overflow
In the above two examples overflow is possible when filling
s. The extraction operator
>> does not restrict the
number of characters accepted. If there are more
than 7 characters in the input stream some of the data stored
may corrupt other memory as shown below:
// Overflow
// overflow.cpp
#include <iostream>
using namespace std;
int main() {
int i;
char c;
double x;
char s[8];
cout << "Enter an integer,\n"
"a character,\n"
"a floating-point number and\n"
"a string : \n";
cin >> i >> c >> x >> s;
cout << "Entered " << endl;
cout << i << ' '
<< c << ' ' << x << ' ' << s << endl;
}
|
Enter an integer,
a character,
a floating-point and
a string :
6 - 9.75 Constantinople
Entered
6 - 2.04952 Constantinople
|
This result varies from platform to platform.
Member Functions
The istream type exposes
the following member functions for controlling extraction:
- ignore(...) - ignores/discards
character(s) from the input buffer
- get(...) - extracts a character
or a string from the input buffer
- getline(...) - extracts a line of
characters from the input buffer
ignore
The ignore() member function extracts characters from
the input buffer and discards the characters. ignore() doesn't skip leading whitespace.
There are two versions of ignore():
cin.ignore();
cin.ignore(2000, '\n');
|
The no-argument version discards a single character. The
two-argument version removes and discards up to the specified number of
characters or up to the specified delimiting character, whichever occurs
first and discards the delimiting character. The default delimiter
is end-of-file (not end-of-line).
get
The get() member function extracts either
a single character or a string from the input buffer.
There are three versions:
// Input Extraction Using get()
// get.cpp
#include <iostream>
using namespace std;
int main() {
char c, d, t[8], u[8], v;
c = cin.get(); // extracts a single character
cin.get(d); // extracts a single character
cin.get(t, 8); // newline delimiter - accepts up to 7 chars
// and adds a null byte
cin.ignore(2000, '\n'); // extracts the 'j' and the newline
cin.get(u, 8, '\t'); // tab delimiter - accepts up to 7 chars and
// adds a null byte
cin.ignore(); // extracts the tab
cin.get(v); // extracts a single character
cout << "c = " << c << endl;
cout << "d = " << d << endl;
cout << "t = " << t << endl;
cout << "u = " << u << endl;
cout << "v = " << v << endl;
}
|
The above program produces the following results:
Input stream : abcdefghij
klmn\topqr
Output:
-------
c = a
d = b
t = cdefghi
u = klmn
v = o
|
get() does not skip leading
whitespace. get(,) leaves the
delimiting character in the input buffer. If we use
get(,) we should remove the delimiting
character, if there is one. Both string versions
- get(char*, int) and get(char*,
int, char) - append a null byte to the sequence of
characters stored in memory.
getline
getline() behaves like get(), but extracts the delimiter from the input
buffer:
// Input Extraction Using getline()
// getline.cpp
#include <iostream>
using namespace std;
int main() {
char t[8], u[8], v;
cin.getline(t, 8); // newline delimiter - accepts up to 7 chars
// and adds a null byte
cin.getline(u, 8, '\t'); // tab delimiter - accepts up to 7 chars and
// adds a null byte
cin.get(v); // extracts a single character
cout << "t = " << t << endl;
cout << "u = " << u << endl;
cout << "v = " << v << endl;
}
|
The above program produces the following results:
Input stream : cdefghi
jklmn\topqr
Output:
-------
t = cdefghi
u = jklmn
v = o
|
getline(), like get(), does not skip leading whitespace and appends
a null byte to the sequence of characters stored in system memory.
Format Control
Manipulators
Manipulators for input objects that don't take arguments are defined in
the <iostream> header file. The one
that takes an argument is defined in <iomanip>.
Manipulators that take no arguments do not include parentheses:
Manipulator |
Effect |
skipws |
skip whitespace |
noskipws |
turn off skip whitespace |
setw(int) |
set the field width for next input (strings
only) |
The argument to setw() should be one more
than the maximum number of input characters to be read.
Note that the setw() manipulator is an
alternative to get(char*, int), but
setw() skips leading whitespace
unless we turn off skipping.
Once a manipulator has modified the format settings of an
input object, those settings remain modified.
We may combine manipulators with input variables
directly. For example,
// Input Manipulators
// manipulator.cpp
#include <iostream>
#include <iomanip>
using namespace std;
int main( ) {
char a[5], b[2], c[7], d[7];
cout << "Enter : ";
cin >> setw(5) >> a >>
setw(2) >> b >> noskipws >>
c >> skipws >> d;
cout << "Stored '" << a <<
"' & '" << b <<
"' & '" << c <<
"' & '" << d << "'" << endl;
}
|
Enter : abcde fgh
Stored 'abcd' & 'e' & '' & 'fgh'
|
State
The ostream and istream
types expose member functions for reporting and changing the state of their
objects.
These member functions include:
- good() - the next operation might
succeed
- eof() - end of data has been
encountered
- fail() - the next operation will
fail
- clear() - reset the state to
good
- bad() - the data may be
corrupted
We should check the state of the input object every time that
we extract a sequence of characters from the input buffer.
If the object encounters an invalid character, the object fails and
leaves that invalid character in the input buffer. The
fail() member function reports such an encounter.
Before the object can continue extracting data, we must clear it of its
failed state. The clear() member function resets
the object's state to good:
if(cin.fail()) { // checks if cin is in a failed state
cin.clear(); // clears state to allow further extraction
cin.ignore(2000, '\n'); // clears the input buffer
}
|
Robust Validation
The state member functions help us validate input robustly.
We check the input object's state with each extraction of a
sequence of characters to ensure that some value was converted and
that the converted value is within valid bounds. We reject
out-of-bound and invalid values, resetting any failed state, and
requesting fresh input as necessary.
getPosInt
To extract from standard input a positive int
that is not greater than max, we write
// getPosInt extracts a positive integer <= max
// from standard input and returns its value
//
int getPosInt(int max) {
int value;
int keepreading;
keepreading = 1;
do {
cout << "Enter a positive integer (<= " << max << ") : ";
cin >> value;
if (cin.fail()) { // check for invalid character
cerr << "Invalid character. Try Again." << endl;
cin.clear();
cin.ignore(2000, '\n');
} else if (value <= 0 || value > max) {
cerr << value << " is outside the range [1," <<
max << ']' << endl;
cerr << "Invalid input. Try Again." << endl;
cin.ignore(2000, '\n');
} else if (char(cin.get()) != '\n') {
cerr << "Trailing characters. Try Again." << endl;
cin.ignore(2000, '\n');
} else
keepreading = 0;
} while(keepreading == 1);
return value;
}
|
Summary
- the insertion and extraction operators accommodate cascading
- field width settings only hold for the next field
- all other settings persist until changed
- precision has different meanings under general,
scientific, and fixed format
- manipulators provide an elegant alternative to member function-based format settings
- manipulators that take arguments require #include <iomanip>
- get() and
getline() can read strings with whitespace
- a failed state must be cleared before processing can continue
Exercises
- Experiment with get(), getline() and
state on little programs of your own
- Complete the Handout on Input and Output
|