Graphical user interfaces (GUIs) have been an integral part of the computing experience for four decades. GUIs support program output in various ways beyond text, including graphs, images, animations, etc. But, depending on the programming language, utilizing these forms requires a deep understanding of the object-oriented paradigm and the APIs of the programming language and the host operating system. Despite GUI's dominance in modern programming, it's still necessary to understand how to format program output.
C++ uses stream objects to read and write data. This section focuses on output stream objects - instances of the ostream class. Output stream objects save data to a file, formatting it as it moves from the program to the file. cout is a specialized output stream object that "saves" or displays its output to the console. Although cout is permanently attached to the console, it is an otherwise typical output stream, suggesting that programmers may use the features described here with all output streams.
How stream objects format data depends on their current configuration or settings. They maintain most of their settings with a set of 1-bit flags. Each flag is a binary switch - on or off - controlling some aspect of the output format. Stream objects group the flags into five functional sets of varying bit lengths. Streams always set default values for each flag, but programmers can dynamically (i.e., while a program is running) change the flags with the functions, manipulators, and symbolic constants described here.
The section begins by introducing or reviewing some syntax and fundamental concepts. The discussions present the concepts using the I/O features without giving them much detail - the focus is on the concepts and syntax. Following the concepts and syntax presentation, the section presents detailed descriptions of the features and reinforces them with programming examples. Please note that most examples adjust the vertical spacing in the program's output to align with the program's corresponding output statements.
Program | Output |
---|---|
#include <iostream> using namespace std; int main() { cout << "See the quick red "; cout << "fox jump over the "; cout << "lazy brown dog." << endl; cout << endl; cout << "See the quick red" << endl; cout << "fox jump over the" << endl; cout << "lazy brown dog." << endl; cout << endl << endl << "See the quick red" << endl; cout << "fox jump" << endl << "over the" << endl; cout << "lazy brown dog." << endl; return 0; } |
See the quick red fox jump over the lazy brown dog. See the quick red fox jump over the lazy brown dog. See the quick red fox jump over the lazy brown dog. |
Programmers change a stream's configuration by switching the formatting flags on and off - "flipping the switches" - with bitwise operations. They use the bitwise-OR operation to switch bits on and the bitwise-AND to switch them off. The bitwise operations require two operands. One operand is a variable - a bit-vector - where each bit is a switch governing a formatting feature. Sometimes, the bits within a group work together, creating more complex formatting options. The second argument is (typically) a constant representing the specific formatting feature the program is changing. Although programmers can use numeric values to denote a particular formatting setting, they rarely do because numbers convey little meaning in this context: how does "4" change a stream's formatting? To make their code more understandable, programmers universally use bitmasks - a symbolic constant whose value consists of mostly 0s and one 1-bit - 000...010...000
- to adjust stream objects.
Chapter 1 introduced us to non-object-oriented symbolic constants, but I/O streams are objects. The following examples use symbolic constants like ios::hex and ios::fixed. ios (sort for input/output system) is a class name, and :: is the scope resolution operator. Together, they bind symbolic constant names, like hex and fixed, to the ios class. Each flag is an integer, but C++ creates an artificial bitmask data type called fmtflags to represent them. Programmers use the stream flag functions to change a stream object's formatting flags.
Function | Comments | Return |
---|---|---|
fmtflags setf(f) fmtflags setf(f, m) |
Sets the flags in f; doesn't change other settings Sets the flags in f and m; clears all other settings |
Previous settings |
void unsetf(f) | Unsets or clears the flags in f | |
fmtflags flags() fmtflags flags(f) |
Gets the current formatting flags Sets the flags in f and clears the rest |
Previous settings |
Imagine a large room with several independent lighting zones. We can turn the lights in each zone on and off without affecting the lights in the other zones or other rooms. Similarly, we can change some flags in a stream object without changing the other flags, and changing the flags in one object doesn't affect other streams. This observation suggests that we can gradually build the needed flags one flag at a time or change several with one operation while retaining the current values in others.
cout.setf(ios::hex); cout.setf(ios::fixed); |
cout.seft(ios::hex | ios::fixed); |
(a) | (b) |
Programmers use the hex, dec, and oct constants to adjust the base or radix in which the stream displays data. Consequently, the operation setf(ios::dec | ios::oct | ios::hex) is meaningless as it's not possible to display data in multiple bases or radixes simultaneously. However, imagine that var is a variable the program sets somewhere to one of the radix values. In that case, the operation setf(var, ios::dec | ios::oct | ios::hex) sets the radix flag to the value saved in var and clears all other flags. Similarly, ORing the three flags together makes sense for the operation unsetf(ios::dec | ios::oct | ios::hex), clearing all three flags. The presence of ios::dec in the unsetf argument notwithstanding, the stream displays output in decimal until the program changes the radix to one of the other bases.
Although not apparent from their appearance, manipulators are functions. Unlike "normal" functions, they only work with the inserter and extractor operators, << and >> respectively, in conjunction with the I/O stream objects.
#include <iostream>
to use manipulators without arguments#include <iomanip>
to use manipulators with argumentsManipulator | Description |
---|---|
endl | Insert an end of line \n and flush the output buffer (the last character is a lower-case 'L') |
dec | Display output in decimal |
hex | Display output in hexadecimal |
oct | Display output in octal |
setw(int w) | Sets the output field width to w. |
right | Right-align, pad on the left (used with setw) |
left | Left-align, pad on the right (used with setw) |
fixed | Display numbers in a fixed decimal point notation (no scientific notation) |
scientific | Display numbers in scientific notation |
Function | Description |
---|---|
fill(char pad) | Sets the padding or fill character when the output field is wider than the printed data. The default fill character is a space or blank. Used with setw. The function has the same effect as the setfill manipulator. |
char fill() | Returns the current fill character. |
width(int w) | Sets the output field width to w. |
precision(int digits) | Sets the number of significant digits displayed in the output of floating-point numbers - float , double , and long double - without affecting the appearance of integers. |
get() | Gets one character from the input stream, returning it as an int . |
ignore() | Discards one character from the input stream. ignore is often called without arguments. |
ignore(int n) | Discards n characters from the input stream or all the characters if the stream has fewer than n characters. |
ignore(int n, char d) | Discards characters from the input stream until it discards n characters or the delimiter character, d. |
"Columnar formatting" is not as imposing as it may sound; it means formatting the output as neatly aligned columns, forming a table. The multtab.cpp program introduces the setw manipulator and serves as an example of a problem needing columnar output.
Left Aligned | Right Aligned |
---|---|
x xx xxx |
x xx xxx |
(a) | (b) |
Output stream objects, like cout, typically display data using exactly the number of characters needed, making single data output relatively easy. However, to format table columns, programmers must place all data in a column whose width is greater than or equal to the widest data value. The setw manipulator formats displayed or printed data, giving it a minimum output width, called the field width. Data wider than the field width displays fully, taking as much space as needed. But if the data is not as wide as the field width, the output streams pad the unused space with the current fill character, a blank or space by default. Programmers change the default fill character with the setfill manipulator or the fill member function. Once the program adjusts the fill character, the stream continues using it until the program changes it again with another call to setfill or fill.
When the field width is wider than the displayed data, the stream can place the fill characters (the padding) on either side of the data. The default location is on the left, aligning the data on the right. Programmers can change the data alignment with the left and right manipulators, as the following program demonstrates.
Program | Output |
---|---|
#include <iostream> #include <iomanip> using namespace std; int main() { cout << "String" << endl; cout << "|" << "Hello world" << "|" << endl; cout << "|" << setw(20) << "Hello world" << "|" << endl; cout << "|" << left << setw(20) << "Hello world" << "|" << endl; cout << "|" << right << setw(20) << "Hello world" << "|" << endl; cout << endl; cout << "Number" << endl; cout << "|" << 123.45 << "|" << endl; cout << "|" << setw(20) << 123.45 << "|" << endl; cout << "|" << left << setw(20) << 123.45 << "|" << endl; cout << "|" << right << setw(20) << 123.45 << "|" << endl; return 0; } |
String |Hello world| | Hello world| |Hello world | | Hello world| Number |123.45| | 123.45| |123.45 | | 123.45| |
Program | Output |
---|---|
#include <iostream> #include <iomanip> using namespace std; int main() { cout << "|" << setw(20) << 123.45 << "|" << endl; cout << endl; cout << "|" << setfill('0') << setw(20) << 123.45 << "|" << endl; // (a) cout << "|" << left << setw(20) << 123.45 << "|" << endl; cout << "|" << right << setw(20) << 123.45 << "|" << endl; cout << endl; cout << "|" << setw(20) << 123.45 << "|" << endl; cout << endl; cout << setfill(' ') << "|" << setw(20) << 123.45 << "|" << endl; return 0; } |
| 123.45| |00000000000000123.45| |123.4500000000000000| |00000000000000123.45| |00000000000000123.45| | 123.45| |
cout.fill(' '); cout.width(20); cout << "|" << 123.45 << "|" << endl;
cout << "|" << left << setw(20) << 123.45 << "|" << endl; | |123.4500000000000000| |
(a) | (b) |
cout << "|" << left << 123.45 << setw(20) << "|" << endl; | |123.45|0000000000000000000 |
(c) | (d) |
<< 123.45
converts the numeric data to a string, then the setw(20) left manipulators format the string for output. The previous cout statement sets the fill character to '0': setfill('0').The default format for floating-point numbers, float
, double
, and long double
, is decimal (base-10), six significant digits (aka significant figures or sig figs), and scientific notation for very large or very small (near zero) numbers. Stream objects maintain the number of significant digits to display in a distinct member variable, and the rest as two sets of 1-bit flags. The numerical-base set consists of three fields: dec, hex, and oct. The float-format set consists of two fields: fixed and scientific. The interaction of these latter two fields can produce some unexpected formats, as illustrated by the following example.
The following program is large, providing numerous examples of the formatting features. It is divided into parts, highlighting the impact of the formatting options and allowing better comments. Focus on the combination of the precision setting and the fixed and scientific flags.
Please note the following about the if-statements:
Program | Output | Comments |
---|---|---|
#define _USE_MATH_DEFINES #include <iostream> #include <cmath> using namespace std; int main() { cout << "Default settings" << endl; if (cout.flags() & ios::fixed) cout << "fixed flag set" << endl; else cout << "fixed flag unset" << endl; if (cout.flags() & ios::scientific) cout << "scientific flag set" << endl; else cout << "scientific flag unset" << endl; cout << 123456789 << endl; cout << 123456789. << endl; cout << M_PI << endl; cout << endl; cout << 1.0/3.0 << endl; cout << 10.0/3.0 << endl; cout << 100.0/3.0 << endl; cout << 1.0/300000.0 << endl; cout << endl; |
Default settings fixed flag unset scientific flag unset 123456789 1.23457e+08 3.14159 0.333333 3.33333 33.3333 3.33333e-06 |
Needed for M_PI with the Microsoft compiler. See The cmath Header File True if fixed is set True if scientific is set Integer output Automatic scientific notation Six significant digits: 3, 1, 4, 1, 5, 9 Six significant digits: "333333" Ditto Ditto Automatic scientific notation |
(a) The default numeric format is six significant digits, base-10, and automatic scientific notation for large and small numbers. Precision at default (6); with fixed and scientific unset, precision is the number of significant digits. | ||
cout.precision(2); if (cout.flags() & ios::fixed) cout << "fixed flag set" << endl; else cout << "fixed flag unset" << endl; if (cout.flags() & ios::scientific) cout << "scientific flag set" << endl; else cout << "scientific flag unset" << endl; cout << 123456789 << endl; cout << 123456789. << endl; cout << M_PI << endl; cout << endl; |
fixed flag unset scientific flag unset 123456789 1.2e+08 3.1 |
Two significant digits in the output Integer format unchanged Two significant digits: 1 and 2 Two significant digits: 3 and 1 |
(b) The program changes the output to two significant digits by calling the precision function. Floating-point numbers are rounded, but integer formatting is unchanged. Precision at 2; with fixed and scientific unset, precision is the number of significant digits. | ||
cout.setf(ios::fixed); if (cout.flags() & ios::fixed) cout << "fixed flag set" << endl; else cout << "fixed flag unset" << endl; if (cout.flags() & ios::scientific) cout << "scientific flag set" << endl; else cout << "scientific flag unset" << endl; cout << 123456789 << endl; cout << 123456789. << endl; cout << M_PI << endl; cout << endl; |
fixed flag set scientific flag unset 123456789 123456789.00 3.14 |
Fixed point output (not scientific) Large numbers not printed in scientific notation |
(c) The precision remains set at 2 from the previous block of code. With fixed set and scientific unset, precision is the number of digits following the decimal (or radix) point. | ||
cout << "Hexadecimal output" << endl; cout.setf(ios::scientific); if (cout.flags() & ios::fixed) cout << "fixed flag set" << endl; else cout << "fixed flag unset" << endl; if (cout.flags() & ios::scientific) cout << "scientific flag set" << endl; else cout << "scientific flag unset" << endl; cout << 123456789 << endl; cout << 123456789. << endl; cout << M_PI << endl; cout << endl; |
Hexadecimal output fixed flag set scientific flag set 123456789 0x1.d7p+26 0x1.92p+1 |
Both flags set, "fixed" set above Integers still display in base-10 Hexadecimal (base-16) output Ditto |
(d) Simultaneously setting the fixed and scientific flags causes floating-point numbers to display in hexadecimal or base-16 output but does not affect integers. Displaying floating-point numbers in hexadecimal is uncommon. With fixed and scientific set, precision is the number of digits following the decimal (or radix) point. | ||
cout << "Decimal output" << endl; cout.unsetf(ios::fixed); cout.setf(ios::scientific); if (cout.flags() & ios::fixed) cout << "fixed flag set" << endl; else cout << "fixed flag unset" << endl; if (cout.flags() & ios::scientific) cout << "scientific flag set" << endl; else cout << "scientific flag unset" << endl; cout << 123456789 << endl; cout << 123456789. << endl; cout << M_PI << endl; return 0; } |
Decimal output fixed flag unset scientific flag set 123456789 1.23e+08 3.14e+00 |
Switches off (i.e., sets to 0) the fixed flag Integer format unaffected Scientific notation Small numbers display in scientific notation |
(e) The unset function unsets or switches off the fixed flag but leaves the scientific flag intact, displaying all floating-point numbers in scientific notation. With fixed unset and scientific set, precision is the number of digits following the decimal (or radix) point. |
Program | Output |
---|---|
#include <iostream> using namespace std; int main() { cout << 23.5 << endl; cout << 1000000.0 << endl; cout << 6.0221413e23 << endl; cout << 1.0 / 30000.0 << endl; cout << endl; cout << fixed << 23.5 << endl; cout << fixed << 1000000.0 << endl; cout << fixed << 6.0221413e23 << endl; cout << fixed << 1.0 / 30000.0 << endl; cout << endl; cout << scientific << 23.5 << endl; cout << scientific << 1000000.0 << endl; cout << scientific << 6.0221413e23 << endl; cout << scientific << 1.0 / 30000.0 << endl; return 0; } |
23.5 1e+06 6.02214e+23 3.33333e-05 23.500000 1000000.000000 602214130000000022740992.000000 0.000033 2.350000e+01 1.000000e+06 6.022141e+23 3.333333e-05 |
Some programs must read a data stream one byte or character at a time. For example, the C++ compiler reads individual characters from a source code file while constructing an intermediate program representation it can convert to machine code. The get function, a member of the istream class, performs this operation. Less often, a program may also use get to read characters from the console. Given this description, the get function's most surprising aspect is its return type.
int get(); |
int c = cin.get(); |
int c; while ((c = cin.get()) != EOF)... |
(a) | (b) | (c) |
The illustrated pattern is common and helpful for processing a file's contents one character at a time. get automatically returns EOF when it reaches the end of the file's contents. It's more difficult to signal the end-of-file when reading input from the console (cin). A Ctrl-Z (press and hold the control key while pressing the "Z" key) inserts an EOF in a Windows input stream. However, Unix and Linux systems allow users to configure what character to use for EOF; by default, it is Ctrl-D (probably the same on a Mac).
Sometimes, programs need to skip data present in an input stream - read it from the stream and discard it. The ignore function provides many options for performing this task. The following figure details the three ways programs can call the ignore function, distinguished by the call's arguments.
istream& ignore(int n = 1, int d = EOF); |
ignore() |
ignore(n) |
ignore(n, d) |
(a) | (b) | (c) | (d) |
Programs use version (a) more often, especially when reading from the console. So, for simplicity, the following examples focus on discarding a single newline character from the console input stream. Our first step is understanding when and why a program might need to skip input.
The Setup | The Problem | ||
---|---|---|---|
int counter; // (a) cin >> counter; |
char c1; // (c) cin >> c1; |
||
char c0; // (b) cin >> c0; |
int c2 = cin.get(); // (d) |
#include <iostream> using namespace std; int main() { while (true) { cout << "A\tAdd\n"; cout << "S\tSub\n"; cout << "M\tMult\n"; cout << "D\tDiv\n"; cout << "E\tExit\n"; cout << "Operation: "; char operation; cin >> operation; //cin.ignore(); switch (operation) { case 'A' : cout << 'A' << endl; break; case 'B' : cout << 'B' << endl; break; case 'C' : cout << 'C' << endl; break; case 'D' : cout << 'D' << endl; break; case 'E' : exit(0); break; } } return 0; } |
#include <iostream> using namespace std; int main() { while (true) { cout << "A\tAdd\n"; cout << "S\tSub\n"; cout << "M\tMult\n"; cout << "D\tDiv\n"; cout << "E\tExit\n"; cout << "Operation: "; int operation = cin.get(); cin.ignore(); switch (operation) { case 'A' : cout << 'A' << endl; break; case 'B' : cout << 'B' << endl; break; case 'C' : cout << 'C' << endl; break; case 'D' : cout << 'D' << endl; break; case 'E' : exit(0); break; } } return 0; } |
(a) | (b) |
int getch() | Get and return a character from the console. |
int _getch() | |
int getche() | Get a character from the console, echo it back to the console, and return it. |
int _getche() |