C++基础(三) 格式控制 setf、setiosflags等详解及避坑

C++提供的输出格式,可以简单理解成一个个标志位。我们对输出格式的控制,其实就是将相应的标志位打开、关闭和设置。内部的实现原理,不必太过深究,会用就行。但需要注意,有些标志位只需要打开或者关闭即可,比如左对齐、右对齐,但是,有些标志位则是需要具体数值或者字符,比如设置宽度和填充字符。

对这些标志位的修改(打开或关闭),因为历史原因,方式有很多。究竟哪种方式好,没有定论。但是我们在写程序时,尽量使用同一种设置方式。学习的时候,也只需要学习一种最适合你的即可。

想要控制输出格式,C++提供的方法很多,有人将其分为两种:一种是控制流的成员函数设置,另一种是利用控制符设置函数setiosflags、setfill、setw等。

但是我认为分为三种更合适,setiosflags应该单独作为一种,且最不推荐,为何,后面会做解释。

1、用cout的成员函数进行格式设置

这种方式,需用用到setf函数,该函数有两个版本,一个只含一个参数,另一个含有两个参数。参数为各个控制位对应的枚举值,即我们经常见到的 std::ios_base::left、std::ios_base::showbase等。其对应的取消设置函数为unsetf。

例如:想指定宽度为10,左对齐,不足位用‘*’填充,用十六进制打印出来,且前面要输出进制的前缀。则可以如下:

        int nValue = 10;
        cout.width(10);
        cout.fill('*');
        cout.setf(std::ios_base::showbase);  //可以用"|"运算符
        cout.setf(std::ios_base::hex, std::ios_base::basefield);
        cout.setf(std::ios_base::left, std::ios_base::adjustfield);
        cout << nValue << endl;

执行结果为:

 

setf的注意事项

C++将控制符分为了两类:

第一类是独立的控制符号,如showbase、showpos等,这些,用setf只有一个参数的版本;

另一类,为组合控制符号,如hex、left等,这些,需要用setf两个参数的版本:第一个参数为left等,第二个参数为其对应的组合值如adjustfield等,如下:

如上面的例子,想设置左对齐和十六进制,只能:
          cout.setf(std::ios_base::left, std::ios_base::adjustfield);
          cout.setf(std::ios_base::hex, std::ios_base::basefield);

2、用setiosflags函数设置相应控制位

想使用该函数,需要包含头文件#include <iomanip>

setiosflags函数有一定的限制,只能打开和关闭相应标志位,对于那些需要提供具体数值或者字符的格式,有些无能为力。

其功能,可以理解成和setf类似,只不过它不是cout的成员函数,位于std名称空间,用起来比setf要略微方便一些。

但是!其只有setf单参数有效的控制位有用,比如上面的hex,就没有效果。

setiosflags函数的参数和setf一样,可以用"|"运算符指定多个,也可以连续设置。

上面的例子,将左对齐改为从中间填充,可以用如下代码实现:

        using std::cout;
        using std::endl;
        int nValue = 10;
        cout.width(10);
        cout.fill('*');
        cout.setf(std::ios_base::hex, std::ios_base::basefield);
    //    cout << std::setiosflags(std::ios_base::showbase | std::ios_base::internal);  //与下面一行代码效果相同
        cout << std::setiosflags(std::ios_base::showbase) << std::setiosflags(std::ios_base::internal);
        cout << nValue << endl;

执行结果为:

为什么单独列出来,因为这个标志位设置函数,有个巨大坑。

setiosflags的局限性

前面已经介绍了控制符号分为两类,setiosflags函数只能设置独立的控制符,即setf单参数版本设置的那些控制符。其他的组合控制符,查阅了很多资料,它好像无能为力。

所以,这里将setiosflags单独列出来,作为一个警告!

3、直接用控制符设置(本人最习惯)

除了以上两种方式外,为了更加便捷操作,可以直接使用控制符。

控制符,也需要加上头文件#include <iomanip>。

实现2中的效果,可以如下实现:

        using std::cout;
        using std::endl;
        int nValue = 10;
        cout << std::setw(10) << std::setfill('*');
        cout << std::hex << std::showbase << std::left;  
        cout << nValue << endl;

执行结果为:

甚至,所有格式控制和最后的nValue输出,可以合并成一行:

cout << std::setw(10) << std::setfill('*') << std::hex << std::showbase << std::internal << nValue << endl;

是不是很方便?

下面,开始介绍各个控制符的具体含义。

考虑到现在越来越多的程序开始使用宽字节(wchar_t)进行编码,所以后面的例子中将会使用wcout而不是cout。对于格式控制,不管是wcout还是cout,都完全一致。

以下代码如果想要copy至本地编译,请加上两行代码: using std::endl;   using std::wcout;

(1)setw、setfill、left、internal、right、showpos、noshowpos

这七个控制符里面,前五个是使用频率最高的,后两个是为了说明internal加到此处的。

这里面,只有setw设置后仅对下一个项目(可以理解成下一个有效输出)有效,其他的控制符一经设置一直有效,直到修改与其关联的控制符。

setw:设置宽度,为函数,参数为int型数值;

setfill:设置填充字符,为函数,参数为字符,默认填充字符是空格;

left:左对齐,即数字显示在左边,不够的再用setfill设置的字符填充;

right:右对齐,即数字显示在右边,不够的再在左侧用setfill设置的字符填充;默认对齐方式是右对齐;

internal:填充字符放到符号(正负号)和数字中间;

showpos:显示非负数字前的正号(+,pos为positive的缩写);

noshowpos:不显示非负数字前的正号,默认的是不显示。

        wcout << L"----------------setw、setfill、left、internal、right、showpos、noshowpos----------------" << endl;
        double dValue1 = -23.457;
        double dValue2 = 3.56;
        wcout << std::setfill(L'*'); 
        int nWidth = 10;  
        wcout << std::setw(nWidth) << dValue2 << endl;  
        wcout << std::setw(nWidth) << std::left << dValue1 << endl;        
        wcout << std::setw(nWidth) << std::right << dValue1 << endl;
        wcout << std::setw(nWidth) << std::internal << std::showpos << dValue2 << endl; 
        wcout << std::right << std::noshowpos;

执行结果:

 

(2)setprecision、fixed、scientific

setprecision:设置数字精度;

fixed:浮点数设置成普通的小数显示;

scientific:浮点数用科学计数法。

首先看一下fixed和scientific,普通的小数表示和科学计数法,很简单。

        double dValue = 2563.54785243568914255;
        wcout << dValue << endl;
        wcout << std::scientific << dValue << endl;

执行结果为:

setprecision需要注意

setprecision,也有一些需要注意的地方,看如下代码:

        double d1 = 2.222222;
        double d2 = 55.555555;
        wcout << std::setprecision(3);
        wcout << d1 << endl;
        wcout << d2 << endl;

执行结果为:

是不是与期待的有点不同?很可能是希望保留两位小数,结果55.555555成了55.6。

这里,setprecision设置的是整个数值的有效位数,而不是小数部分的有效位数。

想要设置固定的有效小数位,怎么办呢?与std::fixed控制符一起使用即可。如下:

        double d1 = 2.222222;
        double d2 = 55.555555;
        wcout << std::setprecision(3) << std::fixed;
        wcout << d1 << endl;
        wcout << d2 << endl;

执行结果为:

保留三位小数,注意,是四舍五入原则

(3)showpoint、noshowpoint

showpoint:对于小数部分为0的浮点数,显示小数点,并且根据设置的精度,补充小数末尾部分的0;

noshowpoint:与showpoint相反

这个控制符也有一些需要注意,如果设置了setprecision和std::fixed(其实只需要设置std::fixed即可,setprecision有默认值,一般为6),即便不设置showpoint,也会显示小数末尾的0。但是仅设置setprecision不行,如:

        double dValue = 22.1;
        wcout << std::setprecision(5) << dValue << endl;  //仅设置了setprecision,不会显示末尾0
        wcout << std::fixed << dValue << endl; 

执行结果为:

如果未设置std::fixed,仅设置std::setprecision,想要显示小数末尾部分的0,则可以用std::showpoint。

需要注意,此时的setprecision表示的精度,不是小数部分,而是整个数值。如下:

        double dValue = 22.1;
        wcout << std::setprecision(5) << std::showpoint << dValue << endl; 

执行结果如下:

(4)oct、dec、hex、showbase以及noshowbase

oct:八进制,前缀为:0;

dec:十进制,没有前缀,为进制的默认值;

hex:十六进制,前缀为:0x

showbase:显示进制的前缀;

noshowbase:不显示进制前缀。

这些控制符,都是一经设置一直有效。

        int nValue_dec = 10;    
        wcout << std::oct << nValue_dec << endl;   //八进制,默认不显示前缀
        wcout << std::showbase << nValue_dec << endl;    //打开前缀,还是输出上一行设置的八进制
        wcout << std::hex << nValue_dec << endl;    //十六进制,已设置过现实显示前缀
        wcout << std::dec << nValue_dec << endl;    //十进制,没有前缀
        wcout << std::noshowbase << nValue_dec << endl;  

执行结果:

(5)boolalpha、noboolalpha

boolalpha:将bool类型的变量输出为'true'或者'false'字样。

boolalpha:将bool类型的变量输出为1和0字样,默认为noboolalpha。
      
        wcout << L"---------------------------boolalpha、noboolalpha----------------------------------" << endl;
        bool bTrue = true;
        bool bFalse = false;
        wcout << bTrue << endl;  //1
        wcout << bFalse << endl;  //0
        wcout << std::boolalpha << bTrue << endl;  //true
        wcout << bFalse << endl; //false
        wcout << std::noboolalpha << bTrue << endl;  //1
 执行结果:

(6)uppercase、nouppercase

uppercase:对于进制前缀、十六进制a-f和浮点数科学计数法的字母e,均显示大写字母;

nouppercase:对于进制前缀、十六进制a-f和浮点数科学计数法的字母e,均显示小写字母,为默认值;

 注意:对其他的(包括浮点数的true和false)无影响。

        int nValue = 10;
        bool bValue = true;
        double dValue = 2.354;
        wcout << std::boolalpha << std::hex << std::showbase << std::scientific;
        wcout << nValue << endl;
        wcout << bValue << endl;
        wcout << dValue << endl;
        wcout << std::uppercase;
        wcout << nValue << endl;
        wcout << bValue << endl;
        wcout << dValue << endl;
        wcout << std::nouppercase;

执行结果为:

 

 

  • 19
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值