原文链接:cout格式化输出
ostream
类是从ios
类派生而来,ios
类是从ios_base
类派生而来。ios_base
类存储了描述格式状态的信息,例如一个类成员中某些位决定使用哪个计数系统(如八/十/十六进制),另外一个成员决定字段的宽度,且ios_base
是ostream
间接基类,因此ostream
也可以修改计数系统和字段宽度。
对于设置显示整数的计数系统整数,我们使用dec
、hex
和oct
控制符来控制整数是以十进制、十六进制还是八进制显示。例如:
int n = 13;
hex(cout); //控制符实际上是函数但不是成员函数,因此不必通过对象来调用,也可以使用cout << hex;
cout << n; //输出d
cout << hex;//等同hex(cout)
cout << n; //输出d
cout << oct;//等同oct(cout),将输出显示设置为八进制
cout << n; // 输出15
cout << dec << n;//输出13
ostream
使用width()
成员函数将长度不同的数字放到宽度相同的字段中,该方法的原型如下:
int width(); //该方法返回字段宽度的当前设置
int width(int i); //该方法将字段宽度设置为i个空格,并返回以前的字段宽度值。这样使得能够保存以前的值,以便以后恢复宽度值使用
width()
方法只影响将显示的下一个项目,然后字段的宽度将恢复为默认值。例如:
cout << "字段默认宽度:" << cout.width() << endl;
cout << "12345" << endl;//显示12345,方便查看后面每个对象显示的时候占用的宽度
cout.width(3);//将字段宽度设置为3
cout << 'a' << 'b' << 'c' << endl;
cout.width(2);
cout << "aaa";//测试当字节宽度设置过小,是否影响显示,该语句执行后字符宽度将恢复为默认值0
auto i = cout.width(3);//i=0
auto j = cout.width(4);//由于上一个语句已经将宽度设置为3,因此j=3
auto k = cout.width(1);//k=4
cout << endl;
cout << "i = " << i << endl;
cout << "j = " << j << endl;
cout << "k = " << k << endl;
其对应的输出为
字段默认宽度:0
12345
abc
aaa
i = 0
j = 3
k = 4
从上面的例子可以看出,将字段宽度设置为3后,字符a
显示字符宽度为3,其余位置填充空格,且默认为右对齐。将字段宽度设置为2后,显示字符串"aaa"
,从结果我们可以看到,字符串正常显示,由此可以看出,cout
不会截短数据。当显示完字符串aaa
后,我们将字符宽度设置为3,记录上一项目的字符宽度为i
,从打印结果来看,当显示完字符串aaa
后,宽度自动恢复为默认值0,因此i
输出结果为0。
默认情况下,cout
使用空格填充字段中未被使用的部分,我们在1.2中的例子已经验证过了,那填充字符可以设置吗?答案是肯定的,使用其成员函数fill()
来改变填充字符,例如:
cout.fill('-');
cout << "12345" << endl;
cout.width(2);
cout << 'a' << 'b' << endl;
cout.width(4);
cout << 'a' << 'b' << endl;
输出结果:
12345
-ab
---ab
由输出结果可知,填充字符的设置与字符宽度设置不同的是,新填充的字符将一直有效,直到它更改为止。
C++的默认精度为6位(末尾的0不显示)。cout
的precision()
成员函数可以设置显示精度,例如:
float a = 3.1415;
float b = 0.123456789;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout.precision(2);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
输出结果:
a = 3.1415
b = 0.123457
a = 3.1
b = 0.12
从输出结果可以看出,precision()
也是设置一次,一直有效,直到重新设置为止。
C++使用setf()
成员函数控制小数点被显示时其他几个格式选项,其中cout.setf(std::ios_base::showpoint)
设置cout
打印浮点类型中末尾的0和小数点。例如:
float a = 13.1415;
float b = 0.123456789;
cout.setf(std::ios_base::showpoint);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout.precision(2);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
输出结果:
a = 13.1415
b = 0.123457
a = 13.
b = 0.12
从输出结果可以看出,setf()
也是设置一次,一直有效,直到重新设置为止。
setf()
有两个原型,第一个为:
fmtflags setf(fmtflags); //fmtflags是bitmask类型(一种用来存储各个位值的类型)的typedef名,用于存储格式的标记
该版本的setf()
用来设置单个位控制的格式信息。参数是一个fmtflags
值,指出要设置哪一位。返回值的类型为fmtflags
的数字,指出所有标记以前的设置。例如要将第11位设置为1,则需要传递一个第11位为1的数字,返回值为原来第11位的值。ios_base
类定义了代表位值的常量,下表为其中一部分常用的定义:
常量 | 含义 |
---|---|
ios_base::boolalpha |
输入和输出bool 值,可以为true 或false |
ios_base::showbase |
对于输出,使用C++基数前缀(0,0x) |
ios_base::showpoint |
显示末尾的小数点 |
ios_base::uppercase |
对于16进制输出,使用大写字母,E表示法 |
ios_base::showpos |
在正数前面加上+ |
【注:注意,仅当基数为10时才使用加号。C++将十六进制和八进制都视为无符号的,因此对它们,无需使用符号(然而,有些C++实现可能仍然会显示加号)。】
例子:
using std::cout;
using std::endl;
using std::ios_base;
int size = 40;
int a = 63;
cout.width(size);
cout << "cout default: a = " << a << endl;
cout.width(size);
cout.setf(ios_base::showpos); //显示+
cout << "cout.setf(ios_base::showpos): a = " << a << endl;
cout.width(size);
std::hex(cout); //使用hex
cout << "std::hex(cout): a = " << a << endl;
cout.width(size);
cout.setf(ios_base::uppercase); // 使用大写字母
cout << "cout.setf(ios_base::uppercase): a = " << a << endl;
cout.width(size);
cout.setf(ios_base::showbase); // 使用0X前缀
cout << "cout.setf(ios_base::showbase): a = " << a << endl;
cout.width(size);
cout << "cout default: true = " << true << endl;
cout.width(size);
cout.setf(ios_base::boolalpha);
cout << "cout.setf(ios_base::boolalpha): true = " << true << endl;
输出结果:
cout default: a = 63
cout.setf(ios_base::showpos): a = +63
std::hex(cout): a = 3f
cout.setf(ios_base::uppercase): a = 3F
cout.setf(ios_base::showbase): a = 0X3F
cout default: true = 0X1
cout.setf(ios_base::boolalpha): true = true
第二个setf()
原型接受两个参数,并返回以前的设置:
fmtflags setf(fmtflags, fmtflags);
第一参数和以前一样,也是一个包含了所需设置的fmtflags
值。第二参数指出要清除第一个参数中的哪些位。例如,将第3位设置为1表示以10为基数,将第4位设置为1表示以8为基数,将第5位设置为1表示以16为基数。假设输出是以10为基数的,而要将它设置为以16为基数,则不仅需要将第5位设置为1,还需要将第3位设置为0——这叫作清除位(clearing the bit)。使用函数setf( )
时,要做的工作多些,因为要用第二参数指出要清除哪些位,用第一参数指出要设置哪位。ios_base
类为此定义了常量(如下表所示)。
第一个参数 | 第二个参数 | 含义 |
---|---|---|
ios_base::dec |
ios_base::basefield |
使用基数10 |
ios_base::oct |
ios_base::basefield |
使用基数8 |
ios_base::hex |
ios_base::basefield |
使用基数16 |
ios_base::fixed |
ios_base::floatfield |
使用定点计数法 |
ios_base::scientific |
ios_base::floatfield |
使用科学计数法 |
ios_base::left |
ios_base::adjustfield |
使用左对齐 |
ios_base::right |
ios_base::adjustfield |
使用右对齐 |
ios_base ::internal |
ios_base::adjustfield |
符号或基数前缀左对齐,值右对齐 |
具体地说,要修改基数,可以将常量ios_base::basefield
用作第二参数,将ios_base::hex
用作第一参数。也就是说,下面的函数调用与使用十六进制控制符的作用相同:
cout.setf(ios_base::hex, ios_base::basefield); //与hex(cout);作用相同
其具体使用方法,如下例所示:
cout.setf(ios_base::left, ios_base::adjustfield);//左对齐
cout.setf(ios_base::showpos);//在正数前面加上+
cout.setf(ios_base::showpoint);//显示小数点和末尾的0
cout.precision(3);
//使用科学计数法显示,并保存默认的计数法
ios_base::fmtflags old =
cout.setf(ios_base::scientific, ios_base::floatfield);
cout << "Left Justification:\n";
long n;
for (n = 1; n <= 41; n += 10) {
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
cout.setf(ios_base::internal, ios_base::adjustfield);//居中对齐
//恢复默认计数法
cout.setf(old, ios_base::floatfield);
cout << "Internal Justification:\n";
for (n = 1; n <= 41; n += 10) {
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
cout.setf(ios_base::right, ios_base::adjustfield);//右对齐
cout.setf(ios_base::fixed, ios_base::floatfield);//使用定点计数法
cout << "Right Justification:\n";
for (n = 1; n <= 41; n += 10) {
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
输出结果:
Left Justification:
+1 |+1.000e+00 |
+11 |+3.317e+00 |
+21 |+4.583e+00 |
+31 |+5.568e+00 |
+41 |+6.403e+00 |
Internal Justification:
+ 1|+ 1.00|
+ 11|+ 3.32|
+ 21|+ 4.58|
+ 31|+ 5.57|
+ 41|+ 6.40|
Right Justification:
+1| +1.000|
+11| +3.317|
+21| +4.583|
+31| +5.568|
+41| +6.403|
【注:对于setf()
的效果可以通过unsetf()
消除,其原型为void unsetf(fmtflags mask);
。其中,mask是位模式。mask中所有的位都设置为1,将使得对应的位被复位。也就是说,setf()
将位设置为1,unsetf()
将位恢复为0。】
对于用户来说,使用setf()
进行格式化并不是最友好的方法。为此C++提供了多个控制符来完成相应的格式化效果,其能够调用setf()
,并自动提供正确的参数。例如我们前面介绍过的dec
、hex
和oct
。C++常用控制符如下表所示:
控制符 | 调用 |
---|---|
boolalpha |
setf(ios_base::boolalpha) |
noboolalpha |
unset(ios_base::noboolalpha) |
showbase |
setf(ios_base::showbase) |
noshowbase |
unsetf(ios_base::showbase) |
showpoint |
setf(ios_base::showpoint) |
noshowpoint |
unsetf(ios_base::showpoint) |
showpos |
setf(ios_base::showpos) |
noshowpos |
unsetf(ios_base::showpos) |
uppercase |
setf(ios_base::uppercase) |
nouppercase |
unsetf(ios_base::uppercase) |
internal |
setf(ios_base::internal, ios_base::adjustfield) |
left |
setf(ios_base::left, ios_base::adjustfield) |
right |
setf(ios_base::right, ios_base::adjustfield) |
dec |
setf(ios_base::dec, ios_base::basefield) |
hex |
setf(ios_base::hex, ios_base::basefield) |
oct |
setf(ios_base::oct, ios_base::basefield) |
fixed |
setf(ios_base::fixed, ios_base::floatfield) |
scientific |
setf(ios_base::scientific, ios_base::floatfield) |
使用iostream
工具来设置一些格式值(如字段宽度)非常麻烦。为了简化工作,C++在头文件中提供了其他的一些控制符,不但可以提供前面提到过的格式设置,而且用起来方便。其中常用的控制符如下:
setprecision()//设置精度,其接受一个指定精度的整数参数
setfill() //填充字符,其接受一个指定填充字符的char参数
setw() //设置字段宽度,其接受一个指定字段宽度的整数参数。
由于它们都是控制符,因此可以用cout
语句连接起来。这样,setw()
控制符在显示多列值时尤其方便。其使用方法如下例所示:
#include
#include
#include
int main() {
using namespace std;
// use new standard manipulators
cout << fixed << right;
// use iomanip manipulators
cout << setw(6) << "N" << setw(14) << "square root" << setw(15)
<< "fourth root\n";
double root;
for (int n = 10; n <= 100; n += 10) {
root = sqrt(double(n));
cout << setw(6) << setfill('.') << n << setfill(' ') << setw(12)
<< setprecision(3) << root << setw(14) << setprecision(4) << sqrt(root)
<< endl;
}
return 0;
}
输出结果:
N square root fourth root
....10 3.162 1.7783
....20 4.472 2.1147
....30 5.477 2.3403
....40 6.325 2.5149
....50 7.071 2.6591
....60 7.746 2.7832
....70 8.367 2.8925
....80 8.944 2.9907
....90 9.487 3.0801
...100 10.000 3.1623
【注:最后一行10.000,是使用fixed控制符导致显示末尾的0。】