Bruce Eckel's Thinking in C++, 2nd Ed Contents | Prev | Next

Output stream formatting

The whole goal of this effort, and all these different types of iostreams, is to allow you to easily move and translate bytes from one place to another. It certainly wouldn’t be very useful if you couldn’t do all the formatting with the printf( ) family of functions. In this section, you’ll learn all the output formatting functions that are available for iostreams, so you can get your bytes the way you want them.

The formatting functions in iostreams can be somewhat confusing at first because there’s often more than one way to control the formatting: through both member functions and manipulators. To further confuse things, there is a generic member function to set state flags to control formatting, such as left- or right-justification, whether to use uppercase letters for hex notation, whether to always use a decimal point for floating-point values, and so on. On the other hand, there are specific member functions to set and read values for the fill character, the field width, and the precision.

In an attempt to clarify all this, the internal formatting data of an iostream is examined first, along with the member functions that can modify that data. (Everything can be controlled through the member functions.) The manipulators are covered separately.

Internal formatting data

The class ios (which you can see in the header file <iostream>) contains data members to store all the formatting data pertaining to that stream. Some of this data has a range of values and is stored in variables: the floating-point precision, the output field width, and the character used to pad the output (normally a space). The rest of the formatting is determined by flags, which are usually combined to save space and are referred to collectively as the format flags. You can find out the value of the format flags with the ios::flags( ) member function, which takes no arguments and returns a long ( typedefed to fmtflags) that contains the current format flags. All the rest of the functions make changes to the format flags and return the previous value of the format flags.

fmtflags ios::flags(fmtflags newflags);
fmtflags ios::setf(fmtflags ored_flag);
fmtflags ios::unsetf(fmtflags clear_flag);

fmtflags ios::setf(fmtflags bits, fmtflags field);

The first function forces all the flags to change, which you do sometimes. More often, you change one flag at a time using the remaining three functions.

The use of setf( ) can seem more confusing: To know which overloaded version to use, you must know what type of flag you’re changing. There are two types of flags: ones that are simply on or off, and ones that work in a group with other flags. The on/off flags are the simplest to understand because you turn them on with setf(fmtflags) and off with unsetf(fmtflags). These flags are

on/off flag

effect

ios::skipws

Skip white space. (For input; this is the default.)

ios::showbase

Indicate the numeric base (dec, oct, or hex) when printing an integral value. The format used can be read by the C++ compiler.

ios::showpoint

Show decimal point and trailing zeros for floating-point values.

ios::uppercase

Display uppercase A-F for hexadecimal values and E for scientific values.

ios::showpos

Show plus sign (+) for positive values.

ios::unitbuf

“Unit buffering.” The stream is flushed after each insertion.

ios::stdio

Synchronizes the stream with the C standard I/O system.

For example, to show the plus sign for cout, you say cout.setf(ios::showpos). To stop showing the plus sign, you say cout.unsetf(ios::showpos).

The last two flags deserve some explanation. You turn on unit buffering when you want to make sure each character is output as soon as it is inserted into an output stream. You could also use unbuffered output, but unit buffering provides better performance.

The ios::stdio flag is used when you have a program that uses both iostreams and the C standard I/O library (not unlikely if you’re using C libraries). If you discover your iostream output and printf( ) output are occurring in the wrong order, try setting this flag.

Format fields

The second type of formatting flags work in a group. You can have only one of these flags on at a time, like the buttons on old car radios – you push one in, the rest pop out. Unfortunately this doesn’t happen automatically, and you have to pay attention to what flags you’re setting so you don’t accidentally call the wrong setf( ) function. For example, there’s a flag for each of the number bases: hexadecimal, decimal, and octal. Collectively, these flags are referred to as the ios::basefield. If the ios::dec flag is set and you call setf(ios::hex), you’ll set the ios::hex flag, but you won’t clear the ios::dec bit, resulting in undefined behavior. The proper thing to do is call the second form of setf( ) like this: setf(ios::hex, ios::basefield) . This function first clears all the bits in the ios::basefield, then sets ios::hex. Thus, this form of setf( ) ensures that the other flags in the group “pop out” whenever you set one. Of course, the hex( ) manipulator does all this for you, automatically, so you don’t have to concern yourself with the internal details of the implementation of this class or to even care that it’s a set of binary flags. Later you’ll see there are manipulators to provide equivalent functionality in all the places you would use setf( ).

Here are the flag groups and their effects:

ios::basefield

effect

ios::dec

Format integral values in base 10 (decimal) (default radix).

ios::hex

Format integral values in base 16 (hexadecimal).

ios::oct

Format integral values in base 8 (octal).

ios::floatfield

effect

ios::scientific

Display floating-point numbers in scientific format. Precision field indicates number of digits after the decimal point.

ios::fixed

Display floating-point numbers in fixed format. Precision field indicates number of digits after the decimal point.

“automatic” (Neither bit is set.)

Precision field indicates the total number of significant digits.

ios::adjustfield

effect

ios::left

Left-align values; pad on the right with the fill character.

ios::right

Right-align values. Pad on the left with the fill character. This is the default alignment.

ios::internal

Add fill characters after any leading sign or base indicator, but before the value.

Width, fill and precision

The internal variables that control the width of the output field, the fill character used when the data doesn’t fill the output field, and the precision for printing floating-point numbers are read and written by member functions of the same name.

function

effect

int ios::width(  )

Reads the current width. (Default is 0.) Used for both insertion and extraction.

int ios::width(int n)

Sets the width, returns the previous width.

int ios::fill(  )

Reads the current fill character. (Default is space.)

int ios::fill(int n)

Sets the fill character, returns the previous fill character.

int ios::precision(  )

Reads current floating-point precision. (Default is 6.)

int ios::precision(int n)

Sets floating-point precision, returns previous precision. See ios::floatfield table for the meaning of “precision.”

The fill and precision values are fairly straightforward, but width requires some explanation. When the width is zero, inserting a value will produce the minimum number of characters necessary to represent that value. A positive width means that inserting a value will produce at least as many characters as the width; if the value has less than width characters, the fill character is used to pad the field. However, the value will never be truncated, so if you try to print 123 with a width of two, you’ll still get 123. The field width specifies a minimum number of characters; there’s no way to specify a maximum number.

The width is also distinctly different because it’s reset to zero by each inserter or extractor that could be influenced by its value. It’s really not a state variable, but an implicit argument to the inserters and extractors. If you want to have a constant width, you have to call width( ) after each insertion or extraction.

An exhaustive example

To make sure you know how to call all the functions previously discussed, here’s an example that calls them all:

//: C18:Format.cpp
// Formatting functions
#include <fstream>
using namespace std;
#define D(A) T << #A << endl; A
ofstream T("format.out");

int main() {
  D(int i = 47;)
  D(float f = 2300114.414159;)
  char* s = "Is there any more?";

  D(T.setf(ios::unitbuf);)
//  D(T.setf(ios::stdio);)  // SOMETHING MAY HAVE CHANGED

  D(T.setf(ios::showbase);)
  D(T.setf(ios::uppercase);)
  D(T.setf(ios::showpos);)
  D(T << i << endl;) // Default to dec
  D(T.setf(ios::hex, ios::basefield);)
  D(T << i << endl;)
  D(T.unsetf(ios::uppercase);)
  D(T.setf(ios::oct, ios::basefield);)
  D(T << i << endl;)
  D(T.unsetf(ios::showbase);)
  D(T.setf(ios::dec, ios::basefield);)
  D(T.setf(ios::left, ios::adjustfield);)
  D(T.fill('0');)
  D(T << "fill char: " << T.fill() << endl;)
  D(T.width(10);)
  T << i << endl;
  D(T.setf(ios::right, ios::adjustfield);)
  D(T.width(10);)
  T << i << endl;
  D(T.setf(ios::internal, ios::adjustfield);)
  D(T.width(10);)
  T << i << endl;
  D(T << i << endl;) // Without width(10)

  D(T.unsetf(ios::showpos);)
  D(T.setf(ios::showpoint);)
  D(T << "prec = " << T.precision() << endl;)
  D(T.setf(ios::scientific, ios::floatfield);)
  D(T << endl << f << endl;)
  D(T.setf(ios::fixed, ios::floatfield);)
  D(T << f << endl;)
  D(T.setf(0, ios::floatfield);) // Automatic
  D(T << f << endl;)
  D(T.precision(20);)
  D(T << "prec = " << T.precision() << endl;)
  D(T << endl << f << endl;)
  D(T.setf(ios::scientific, ios::floatfield);)
  D(T << endl << f << endl;)
  D(T.setf(ios::fixed, ios::floatfield);)
  D(T << f << endl;)
  D(T.setf(0, ios::floatfield);) // Automatic
  D(T << f << endl;)

  D(T.width(10);)
  T << s << endl;
  D(T.width(40);)
  T << s << endl;
  D(T.setf(ios::left, ios::adjustfield);)
  D(T.width(40);)
  T << s << endl;

  D(T.unsetf(ios::showpoint);)
  D(T.unsetf(ios::unitbuf);)
//  D(T.unsetf(ios::stdio);) // SOMETHING MAY HAVE CHANGED
} ///:~

This example uses a trick to create a trace file so you can monitor what’s happening. The macro D(a) uses the preprocessor “stringizing” to turn a into a string to print out. Then it reiterates a so the statement takes effect. The macro sends all the information out to a file called T, which is the trace file. The output is

int i = 47;
float f = 2300114.414159;
T.setf(ios::unitbuf);
T.setf(ios::stdio);
T.setf(ios::showbase);
T.setf(ios::uppercase);
T.setf(ios::showpos);
T << i << endl;
+47
T.setf(ios::hex, ios::basefield);
T << i << endl;
+0X2F
T.unsetf(ios::uppercase);
T.setf(ios::oct, ios::basefield);
T << i << endl;
+057
T.unsetf(ios::showbase);
T.setf(ios::dec, ios::basefield);
T.setf(ios::left, ios::adjustfield);
T.fill('0');
T << "fill char: " << T.fill() << endl;
fill char: 0
T.width(10);
+470000000
T.setf(ios::right, ios::adjustfield);
T.width(10);
0000000+47
T.setf(ios::internal, ios::adjustfield);
T.width(10);
+000000047
T << i << endl;
+47
T.unsetf(ios::showpos);
T.setf(ios::showpoint);
T << "prec = " << T.precision() << endl;
prec = 6
T.setf(ios::scientific, ios::floatfield);
T << endl << f << endl;

2.300115e+06
T.setf(ios::fixed, ios::floatfield);
T << f << endl;
2300114.500000
T.setf(0, ios::floatfield);
T << f << endl;
2.300115e+06
T.precision(20);
T << "prec = " << T.precision() << endl;
prec = 20
T << endl << f << endl;

2300114.50000000020000000000
T.setf(ios::scientific, ios::floatfield);
T << endl << f << endl;

2.30011450000000020000e+06
T.setf(ios::fixed, ios::floatfield);
T << f << endl;
2300114.50000000020000000000
T.setf(0, ios::floatfield);
T << f << endl;
2300114.50000000020000000000
T.width(10);
Is there any more?
T.width(40);
0000000000000000000000Is there any more?
T.setf(ios::left, ios::adjustfield);
T.width(40);
Is there any more?0000000000000000000000
T.unsetf(ios::showpoint);
T.unsetf(ios::unitbuf);
T.unsetf(ios::stdio);

Studying this output should clarify your understanding of the iostream formatting member functions.

Contents | Prev | Next


Contact: webmaster@codeguru.com
CodeGuru - the website for developers.