cout格式化输出

2023-02-26 10:59:42 浏览数 (1)

【目录】

  • 1. 修改显示时的计数系统
  • 2. 调整字段宽度
  • 3. 填充字符
  • 4. 设置浮点数的显示精度
  • 5. setf()
  • 6. 标准控制符
  • 7. 头文件iomanip

1. 修改显示时的计数系统

ostream类是从ios类派生而来,ios类是从ios_base类派生而来。ios_base类存储了描述格式状态的信息,例如一个类成员中某些位决定使用哪个计数系统(如八/十/十六进制),另外一个成员决定字段的宽度,且ios_baseostream间接基类,因此ostream也可以修改计数系统和字段宽度。

对于设置显示整数的计数系统整数,我们使用dechexoct控制符来控制整数是以十进制、十六进制还是八进制显示。例如:

代码语言:javascript复制
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

2. 调整字段宽度

ostream使用width()成员函数将长度不同的数字放到宽度相同的字段中,该方法的原型如下:

代码语言:javascript复制
int width(); //该方法返回字段宽度的当前设置
int width(int i); //该方法将字段宽度设置为i个空格,并返回以前的字段宽度值。这样使得能够保存以前的值,以便以后恢复宽度值使用

width()方法只影响将显示的下一个项目,然后字段的宽度将恢复为默认值。例如:

代码语言:javascript复制
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;

其对应的输出为

代码语言:javascript复制
字段默认宽度:0
12345
  abc
aaa
i = 0
j = 3
k = 4

从上面的例子可以看出,将字段宽度设置为3后,字符a显示字符宽度为3,其余位置填充空格,且默认为右对齐。将字段宽度设置为2后,显示字符串"aaa",从结果我们可以看到,字符串正常显示,由此可以看出,cout不会截短数据。当显示完字符串aaa后,我们将字符宽度设置为3,记录上一项目的字符宽度为i,从打印结果来看,当显示完字符串aaa后,宽度自动恢复为默认值0,因此i输出结果为0。

3. 填充字符

默认情况下,cout 使用空格填充字段中未被使用的部分,我们在1.2中的例子已经验证过了,那填充字符可以设置吗?答案是肯定的,使用其成员函数fill()来改变填充字符,例如:

代码语言:javascript复制
cout.fill('-');
cout << "12345" << endl;
cout.width(2);
cout << 'a' << 'b' << endl;
cout.width(4);
cout << 'a' << 'b' << endl;

输出结果:

代码语言:javascript复制
12345
-ab
---ab

由输出结果可知,填充字符的设置与字符宽度设置不同的是,新填充的字符将一直有效,直到它更改为止。

4. 设置浮点数的显示精度

C 的默认精度为6位(末尾的0不显示)。coutprecision()成员函数可以设置显示精度,例如:

代码语言:javascript复制
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;

输出结果:

代码语言:javascript复制
a = 3.1415
b = 0.123457
a = 3.1
b = 0.12

从输出结果可以看出,precision()也是设置一次,一直有效,直到重新设置为止。

5. setf()

C 使用setf()成员函数控制小数点被显示时其他几个格式选项,其中cout.setf(std::ios_base::showpoint)设置cout打印浮点类型中末尾的0和小数点。例如:

代码语言:javascript复制
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;

输出结果:

代码语言:javascript复制
a = 13.1415
b = 0.123457
a = 13.
b = 0.12

从输出结果可以看出,setf()也是设置一次,一直有效,直到重新设置为止。

setf()有两个原型,第一个为:

代码语言:javascript复制
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 实现可能仍然会显示加号)。】

例子:

代码语言:javascript复制
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;

输出结果:

代码语言:javascript复制
                     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()原型接受两个参数,并返回以前的设置:

代码语言:javascript复制
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用作第一参数。也就是说,下面的函数调用与使用十六进制控制符的作用相同:

代码语言:javascript复制
cout.setf(ios_base::hex, ios_base::basefield); //与hex(cout);作用相同

其具体使用方法,如下例所示:

代码语言:javascript复制
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";
}

输出结果:

代码语言:javascript复制
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。】

6. 标准控制符

对于用户来说,使用setf()进行格式化并不是最友好的方法。为此C 提供了多个控制符来完成相应的格式化效果,其能够调用setf(),并自动提供正确的参数。例如我们前面介绍过的dechexoct。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)

7. 头文件iomanip

使用iostream工具来设置一些格式值(如字段宽度)非常麻烦。为了简化工作,C 在头文件中提供了其他的一些控制符,不但可以提供前面提到过的格式设置,而且用起来方便。其中常用的控制符如下:

代码语言:javascript复制
 setprecision()//设置精度,其接受一个指定精度的整数参数
 setfill()     //填充字符,其接受一个指定填充字符的char参数
 setw()        //设置字段宽度,其接受一个指定字段宽度的整数参数。

由于它们都是控制符,因此可以用cout语句连接起来。这样,setw()控制符在显示多列值时尤其方便。其使用方法如下例所示:

代码语言:javascript复制
#include <cmath>
#include <iomanip>
#include <iostream>
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 rootn";
    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;
}

输出结果:

代码语言:javascript复制
     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。】

0 人点赞