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
|
|
|
Skip
white space. (For input; this is the default.)
|
|
Indicate
the numeric base (dec, oct, or hex) when printing an integral value. The format
used can be read by the C++ compiler.
|
|
Show
decimal point and trailing zeros for floating-point values.
|
|
Display
uppercase A-F for hexadecimal values and E for scientific values.
|
|
Show
plus sign (+) for positive values.
|
|
“Unit
buffering.” The stream is flushed after each insertion.
|
|
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:
|
|
|
Format
integral values in base 10 (decimal) (default radix).
|
|
Format
integral values in base 16 (hexadecimal).
|
|
Format
integral values in base 8 (octal).
|
|
|
|
Display
floating-point numbers in scientific format. Precision field indicates number
of digits after the decimal point.
|
|
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.
|
|
|
|
Left-align
values; pad on the right with the fill character.
|
|
Right-align
values. Pad on the left with the fill character. This is the default alignment.
|
|
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.
|
|
|
Reads
the current width. (Default is 0.) Used for both insertion and extraction.
|
|
Sets
the width, returns the previous width.
|
|
Reads
the current fill character. (Default is space.)
|
|
Sets
the fill character, returns the previous fill character.
|
|
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.
Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru