本文摘自C++primer第四版附录A3
A.3.3. 控制输出格式
许多操纵符使我们能够改变输出的外观。有两大类的输出控制:控制数值的表示,以及控制填充符的数量和布局。
控制布尔值和格式
改变对象格式化状态的操纵符的一个例子是 boolalpha 操纵符。默认情况下,将 bool 值显示为 1 或 0,true 值显示为 1,而 false值显示为 0。可以通过流的 boolalpha 操纵符覆盖这个格式化:
cout << "default bool values: " << true << " " << false << "\nalpha bool values: " << boolalpha << true << " " << false << endl;
执行时,这段程序产生下面的输出:
default bool values: 1 0 alpha bool values: true false
一旦将 boolalpha “写”至 cout,从这个点起就改变了 cout 将怎样显示 bool 值,后续显示 bool 值的操作将用 true 或 false 进行显示。
要取消 cout 的格式状态改变,必须应用 noboolalpha:
bool bool_val; cout << boolalpha // sets internal state of cout << bool_val << noboolalpha; // resets internal state to default formatting
现在只改变 bool 值的格式化来显示 bool_val,并且立即将流重置为原来的状态。
指定整型值的基数
默认情况下,用十进制读写整型值。通过使用操纵符 hex、oct 和 dec,程序员可以将表示进制改为八进制、十六进制或恢复十进制(浮点值的表示不受影响):
const int ival = 15, jval = 1024; // const, so values never change cout << "default: ival = " << ival << " jval = " << jval << endl; cout << "printed in octal: ival = " << oct << ival << " jval = " << jval << endl; cout << "printed in hexadecimal: ival = " << hex << ival << " jval = " << jval << endl; cout << "printed in decimal: ival = " << dec << ival << " jval = " << jval << endl;
编译和执行的时候,程序产生下面的输出:
default: ival = 15 jval = 1024 printed in octal: ival = 17 jval = 2000 printed in hexadecimal: ival = f jval = 400 printed in decimal: ival = 15 jval = 1024
注意,像 boolalpha 一样,这些操纵符改变格式状态。它们影响紧接在后面的输出,以及所有后续的整型输出,直到通过调用另一操纵符重围格式为止。
指出输出的基数
默认情况下,显示数值的时候,不存在关于所用基数的可见记号。例如,20 是 20,还是 16 的八进制表示?按十进制模式显示数值的时候,会按我们期待的格式打印数值。如果需要打印八进制或十六进制值,可能应该也使用 showbase 操纵符。showbase 操纵符导致输出流使用的约定,与指定整型常量基数所用的相同:
-
以 0x 为前导表示十六进制。
-
以 0 为前导表示八进制。
-
没有任何前导表示十进制。
修改程序使用 showbase 如下:
const int ival = 15, jval = 1024; // const so values never change cout << showbase; // show base when printing integral values cout << "default: ival = " << ival << " jval = " << jval << endl; cout << "printed in octal: ival = " << oct << ival << " jval = " << jval << endl; cout << "printed in hexadecimal: ival = " << hex << ival << " jval = " << jval << endl; cout << "printed in decimal: ival = " << dec << ival << " jval = " << jval << endl; cout << noshowbase; // reset state of the stream
修改后的输出使得基础值到底是什么很清楚:
default: ival = 15 jval = 1024 printed in octal: ival = 017 jval = 02000 printed in hexadecimal: ival = 0xf jval = 0x400 printed in decimal: ival = 15 jval = 1024
noshowbase 操纵符重置 cout,以便它不再显示整型值的表示基数。
默认情况下,十六进制值用带小写 x 的小写形式打印。可以应用 uppercase 操纵符显示 X 并将十六进制数字 a - f 显示为大写字母。
cout << uppercase << showbase << hex << "printed in hexadecimal: ival = " << ival << " jval = " << jval << endl << nouppercase << endl;
前面的程序产生下面的输出:
printed in hexadecimal: ival = 0XF jval = 0X400
要恢复小写,就应用 nouppercase 操纵符。
控制浮点值的格式
对于浮点值的格式化,可以控制下面三个方面:
-
精度:显示多少位数字。
-
记数法:用小数还是科学记法法显示。
-
对是整数的浮点值的小数点的处理。
默认情况下,使用六位数字的精度显示浮点值。如果值没有小数部分,则省略小数点。使用小数形式还是科学记数法显示数值取决于被显示的浮点数的值,标准库选择增强数值可读性的格式,非常大和非常小的值使用科学记数法显示,其他值使用小数形式。
指定显示精度
默认情况下,精度控制显示的数字总位数。显示的时候,将浮点值四舍五入到当前精度。因此,如果当前精度是 4,则 3.14159 成为3.142;如果精度是 3,打印为 3.14。
通过名为 precision 的成员函数,或者通过使用 setprecision 操纵符,可以改变精度。precision 成员是重载的(第 7.8 节):一个版本接受一个 int 值并将精度设置为那个新值,它返回先前的精度值;另一个版本不接受实参并返回当前精度值。setprecision 操纵符接受一个实参,用来设置精度。
下面的程序说明控制显示浮点值所用精度的不同方法:
// cout.precision reports current precision value cout << "Precision: " << cout.precision() << ", Value: " << sqrt(2.0) << endl; // cout.precision(12) asks that 12 digits of precision to be printed cout.precision(12); cout << "Precision: " << cout.precision() << ", Value: " << sqrt(2.0) << endl; // alternative way to set precision using setprecision manipulator cout << setprecision(3); cout << "Precision: " << cout.precision() << ", Value: " << sqrt(2.0) << endl;
编译并执行后,程序产生下面的输出:
Precision: 6, Value: 1.41421 Precision: 12, Value: 1.41421356237 Precision: 3, Value: 1.41
这个程序调用标准库中的 sqrt 函数,可以在头文件 cmath 中找到它。sqrt 函数量重载的,可以用 float、double 或 long double实参调用,它返回实参的平方根。
操纵符和其他接受实参的操纵符定义在头文件 iomanip 中。 |
控制记数法
默认情况下,用于显示浮点值的记数法取决于数的大小:如果数很大或很小,将按科学记数法显示,否则,使用固定位数的小数。标准库选择使得数容易阅读的记数法。
将浮点数显示为普通数(相对于显示货币、百分比,那时我们希望控制值的外观)的时候,通常最好让标准库来选择使用的记数法。要强制科学记数法或固定位数小数的一种情况是在显示表的时候,表中的小数点应该对齐。 |
如果希望强制科学记数法或固定位数小数表示,可以通过使用适当的操纵符做到这一点:scientific 操纵符将流变为使用科学记数法。像在十六进制值上显示 x 一样,也可以通过 uppercase 操纵符控制科学记数法中的 e。fixed 操纵符将流为使用固定位数小数表示。
这些操纵符改变流精度的默认含义。执行 scientific 或 fixed 之后,精度值控制小数点之后的数位。默认情况下,精度指定数字的总位数——小数点之前和之后。使用 fixed 或 scientific 命名我们能够按列对齐来显示数,这一策略保证小数点总是在相对于被显示的小数部分固定的位置。
恢复浮点值的默认记数法
与其他操纵符不同,不存在将流恢复为根据被显示值选择记数法的默认状态的操纵符,相反,我们必须调用 unsetf 成员来取消scientific 或 fixed 所做的改变。要将流恢复为浮点值的默认处理,将名为 floatfield 的标准库定义值传给 unsetf 函数:
// reset to default handling for notation cout.unsetf(ostream::floatfield);
除了取消它们的效果之外,使用这些操纵符像使用任意其他操纵符一样:
cout << sqrt(2.0) << '\n' << endl; cout << "scientific: " << scientific << sqrt(2.0) << '\n' << "fixed decimal: " << fixed << sqrt(2.0) << "\n\n"; cout << uppercase << "scientific: " << scientific << sqrt(2.0) << '\n' << "fixed decimal: " << fixed << sqrt(2.0) << endl << nouppercase; // reset to default handling for notation cout.unsetf(ostream::floatfield); cout << '\n' << sqrt(2.0) << endl;
产生如下输出:
1.41421 scientific: 1.414214e+00 fixed decimal: 1.414214 scientific: 1.414214E+00 fixed decimal: 1.414214 1.41421
显示小数点
默认情况下,当浮点值的小数部分为 0 的时候,不显示小数点。showpoint 操纵符强制显示小数点:
cout << 10.0 << endl; // prints 10
cout << showpoint << 10.0 // prints 10.0000
<< noshowpoint << endl; // revert to default handling of decimal point
noshowpoint 操纵符恢复默认行为。下一个输出表达式将具有默认行为,即,如果浮点值小数部分为 0,就取消小数点。
填充输出
按栏显示数据的时候,经常很希望很好地控制数据的格式化。标准库提供下面几个操纵帮助我们实现需要的控制:
-
setw,指定下一个数值或字符串的最小间隔。
-
left,左对齐输出。
right,右对齐输出。输出默认为右对齐。
-
internal,控制负值的符号位置。internal 左对齐符号且右对齐值,用空格填充介于其间的空间。
-
setfill,使我们能够指定填充输出时使用的另一个字符。默认情况下,值是空格。
像 endl 一样,setw 不改变输出流的内部状态,它只决定下一个输出的长度。 |
下面程序段说明了这些操纵符:
int i = -16; double d = 3.14159; // pad first column to use minimum of 12 positions in the output cout << "i: " << setw(12) << i << "next col" << '\n' << "d: " << setw(12) << d << "next col" << '\n'; // pad first column and left-justify all columns cout << left << "i: " << setw(12) << i << "next col" << '\n' << "d: " << setw(12) << d << "next col" << '\n' << right; // restore normal justification // pad first column and right-justify all columns cout << right << "i: " << setw(12) << i << "next col" << '\n' << "d: " << setw(12) << d << "next col" << '\n'; // pad first column but put the padding internal to the field cout << internal << "i: " << setw(12) << i << "next col" << '\n' << "d: " << setw(12) << d << "next col" << '\n'; // pad first column, using # as the pad character cout << setfill('#') << "i: " << setw(12) << i << "next col" << '\n' << "d: " << setw(12) << d << "next col" << '\n' << setfill(' '); // restore normal pad character
执行时,该程序段产生如下输出:
i: -16next col d: 3.14159next col i: -16 next col d: 3.14159 next col i: -16next col d: 3.14159next col i: - 16next col d: 3.14159next col i: -#########16next col d: #####3.14159next col
A.3.4. 控制输入格式化
默认情况下,输入操作符忽略空白(空格、制表符、换行符、进纸和回车)。对下面的循环:
while (cin >> ch) cout << ch;
给定输入序列
a b c d
循环执行四次从字符 a 读到 d,跳过介于其间的空格、可能的制表符和换行符。该程序段的输出是:
abcd
noskipws 操纵符导致输入操作符读(而不是跳过)空白。要返回默认行为,应用 skipws 操纵符:
cin >> noskipws; // set cin so that it reads whitespace while (cin >> ch) cout << ch; cin >> skipws; // reset cin to default state so that it discards whitespace
给定与前面相同的输入,该循环进行 7 次迭代,读输入中的空白以及字符。该循环产生如下输出:
a b c d
A.3.5. 未格式化的输入/输出操作
迄今为止,示例程序中只使用过格式化的 IO 操作。输入和输出操作符(<< 和 >>)根据被处理数据的类型格式化所读写的数据。输入操作符忽略空白,输出操作符应用填充、精度等。
标准库还提供了丰富的支持未格式化 IO 的低级操作,这些操作使我们能够将流作为未解释的字节序列处理,而不是作为数据类型(如char、int、string 等)的序列处理。