❥关于C++之用cout进行格式化

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❥关于C++之用cout进行格式化相关的知识,希望对你有一定的参考价值。

#include <iostream>
using namespace std;
int main() 
	int i = 273;
	cout << i << endl;
	cout << -i << endl;
	cout << "————————————" << endl;
	double d1 = 1.200;
	cout << d1 << endl;
	cout << (d1 + 1.0) / 9.0 << endl;
	cout << "————————————" << endl;
	double d2 = 1.67E2;
	cout << d2 << endl;
	d2 += 1.0 / 9.0;
	cout << d2 << endl;
	cout << d2 * 1.0e4 << endl;
	cout << "————————————" << endl;
	double d3 = 2.3e-4;
	cout << d3 << endl;
	cout << d3 / 10 << endl;
	return 0;
273
-273
————————————
1.2
0.244444
————————————
167
167.111
1.67111e+06
————————————
0.00023
2.3e-05

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

要控制整数以十进制、十六进制还是八进制显示,可以使用dec、hex和oct控制符。

这句函数调用将cout对象的计数系统格式状态设置为十六进制:hex(cout); 完成设置后,程序将以十六进制形式打印整数值,直到将格式状态设置为其他选项为止。ostream类重载了<<运算符,这使得cout<<hex;与函数调用hex(cout)等价。

#include <iostream>
using namespace std;
int main() 
	int n = 13;
	cout << n << " (decimal)" << endl;
	cout << hex;// set to hex mode
	cout << n << " (hexadecimal)" << endl;
	cout << oct;// set to octal mode
	cout << n << " (octal)" << endl;
	cout << dec;// reset to decimal mode
	cout << n << " (decimal)" << endl;
	return 0;
13 (decimal)
d (hexadecimal)
15 (octal)
13 (decimal)

2.调整字段宽度

get (1)	streamsize width() const;
set (2)	streamsize width(streamsize wide);

第一种格式返回字段宽度的当前设置;第二种格式将字段宽度设置为i个空格,并返回以前的字段宽度值。这使得能够保存以前的值,以便以后恢复宽度值时使用。

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

#include <iostream>
using namespace std;
int main() 
	cout << "#";
	cout.width(12);
	cout << 12 << "#" << 24 << "#" << endl;
	return 0;
#          12#24#

数字12被放到宽度为12个字符的字段的最右边,这被称为右对齐。然后,字段宽度恢复为默认值,并将两个#符号以及24放在宽度与它们的长度相等的字段中。默认填充是空格,默认对齐是右对齐。

注:C++永远不会截短数据,因此如果试图在宽度为2的字段中打印一个7位值,C++将增宽字段,以容纳该数据。

3.填充字符

在默认情况下,cout用空格填充字段中未被使用的部分,可以用fill()成员函数来改变填充字符。

#include <iostream>
using namespace std;
int main() 
	cout.fill('*');
	long bonus[2] = 900, 13508;
	for (int i = 0; i < 2; i++) 
		cout << "$";
		cout.width(7);
		cout << bonus[i] << endl;
	
	return 0;
$****900
$**13508

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

浮点数精度的含义取决于输出模式。在默认模式下,它指的是显示的总位数。在定点模式和科学模式下,精度指的是小数点后面的位数。已经知道,C++的默认精度为6位(但末尾的0将不显示)。precision()成员函数使得能够选择其他值。

#include <iostream>
using namespace std;
int main() 
	float price1 = 20.40;
	float price2 = 1.9 + 8.0 / 9.0;
	cout << "price1 = $" << price1 << endl;
	cout << "price2 = $" << price2 << endl;
	cout.precision(2);
	cout << "now price1 = $" << price1 << endl;
	cout << "now price2 = $" << price2 << endl;
	return 0;
price1 = $20.4
price2 = $2.78889
now price1 = $20
now price2 = $2.8

5.打印末尾的0和小数点

对于有些输出(如价格或栏中的数字),保留末尾的0将更为美观。例如,对于上述程序清单的输出,$20.40将比$20.4更美观。

ios_base类提供了一个setf()函数(用于set标记),能够控制多种格式化特性。这个类还定义了多个常量,可用作该函数的参数。例如,下面的函数调用使cout显示末尾小数点:

cout.setf(ios_base::showpoint);

使用默认的浮点格式时,上述语句还将导致末尾的0被显示出来。也就是说,如果使用默认精度(6位)时,cout不会将2.00显示为2,而是将它显示为2.000000。

showpoint是ios_base类声明中定义的类级静态常量。类级意味着如果在成员函数定义的外面使用它,则必须在常量名前面加上作用域运算符(::)。因此ios_base::showpoint指的是在ios_base类中定义的一个常量。

#include <iostream>
using namespace std;
int main() 
	float price1 = 20.40;
	float price2 = 1.9 + 8.0 / 9.0;
	cout.setf(ios_base::showpoint);
	cout << "price1 = $" << price1 << endl;
	cout << "price2 = $" << price2 << endl;
	cout.precision(2);
	cout << "now price1 = $" << price1 << endl;
	cout << "now price2 = $" << price2 << endl;
	return 0;
price1 = $20.4000  // 默认精度6位
price2 = $2.78889  // 默认精度6位
now price1 = $20.  // 设置的精度2位
now price2 = $2.8  // 设置的精度2位

在上述输出中,第一行显示了;第三行显示了小数点,但没有显示末尾的0,这是因为精度被设置为2,而小数点前面已经包含两位。

6.再谈setf()

ios_base类有一个受保护的数据成员,其中的各位标记分别控制着格式化的各个方面,例如计数系统、是否显示末尾的0等。打开一个标记称为设置标记(或位),并意味着相应的位被设置为1。位标记是编程开关,相当于设置DIP开关以配置计算机硬件。例如,hex、dec和oct控制符调整控制计数系统的3个标记位。setf()函数提供了另一种调整标记位的途径。

set (1)	 fmtflags setf (fmtflags fmtfl);
mask (2) fmtflags setf (fmtflags fmtfl, fmtflags mask);

bitmask类型(位掩码类型)是一种用来存储各个位值的类型。它可以是整型、枚举,也可以是STL bitset容器。这里的主要思想是,每一位都是可以单独访问的,都有自己的含义。iostream软件包使用bitmask来存储状态信息


std::ios_base::fmtflags

表示流格式标志的位掩码类型。
此类型由成员函数flags、setf和unset用作其参数和/或返回值。

fmtflags是bitmask类型的typedef名,用于存储格式标记。该名称是在ios_base类中定义的。这个版本的setf()用来设置单个位控制的格式信息。参数是一个fmtflags值,指出要设置哪一位。返回值是类型为fmtflags的数字,指出所有标记以前的设置。如果打算以后恢复原始设置,则可以保存这个值。应给setf()传递什么呢?如果要第11位设置为1,则可以传递一个第11位为1的数字。返回值的第11位将被设置为1。对位进行跟踪好像单调乏味(实际上也是这样)。然而,你不必作做这项工作,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在正数前面加上“+”

#include <iostream>
using namespace std;
int main() 
	int temperature = 63;
	cout.setf(ios_base::showpos);// show plus sign
	cout << temperature << endl;
	cout << hex << temperature << endl;// use hex
	cout.setf(ios_base::uppercase);// use uppercase in hex
	cout.setf(ios_base::showbase);// use 0X prefix for hex
	cout << "check result is:" << true << endl;
	cout.setf(ios_base::boolalpha);// can show true/false
	cout << "check result is:" << true << endl;
	return 0;
+63
3f
check result is:0X1
check result is:true

注意,仅当基数为10时才使用加号。C++将十六进制和八进制都视为无符号的,因此对它们,无需使用符号(然而,有些C++实现可能仍然会显示加号)。

第二个setf()原型接受两个参数,并返回以前的设置fmtflags setf(fmtflags fmtfl, fmtflags mask);

函数的这种重载格式用于设置由多位控制的格式选项。第一参数和以前一样,也是一个包含了所需设置的fmtflags值。第二参数指出要清除第一个参数中的哪些位

例如,将第3位设置为1表示以10为基数,将第4位设置为1表示以8为基数,将第5位设置为1表示以16为基数。假设输出是以10为基数的,而要将它设置为以16为基数,则不仅需要将第5位设置为1,还需要将第3位设置为0——这叫作清除位(clearing the bit)。聪明的十六进制控制符可自动完成这两项任务。使用函数setf()时,要做的工作多些,因为要用第二参数指出要清除哪些位,用第一参数指出要设置哪位。

这并不像听上去那么复杂,因为ios_base类为此定义了常量(如下表所示)。具体地说,要修改基数,可以将常量ios_base::basefield用作第二参数,将ios_base::hex用作第一参数。也就是说,下面的函数调用与使用十六进制控制符的作用相同:

cout.setf(ios_base::hex, ios_base::basefield);

ios_base类定义了可按这种方式处理的3组格式标记。每组标记都由一个可用作第二参数的常量和两三个可用作第一参数的常量组成。第二参数清除一批相关的位,然后第一参数将其中一位设置为1。上表列出了用作setf()的第二参数的常量的名称、可用作第一参数的相关常量以及它们的含义。

例如,要选择左对齐,可将ios_base::adjustfield用作第二参数,将ios_base::left作为第一参数。左对齐意味着将值放在字段的左端,右对齐则表示将值放在字段的右端。内部对齐表示将符号或基数前缀放在字段左侧,余下的数字放在字段的右侧(遗憾的是,C++没有提供自对齐模式)。

定点表示法意味着使用格式123.4来表示浮点值,而不管数字的长度如何,科学表示法则意味着使用格式1.23e04,而不考虑数字的长度。如果你熟悉C语言中printf()的说明符,则可能知道,默认的C++模式对应于%g说明符,定点表示法对应于%f说明符,而科学表示法对应于%e说明符。

在C++标准中,定点表示法和科学表示法都有下面两个特征:

  • 精度指的是小数位数,而不是总位数;
  • 显示末尾的0。

precision虽然是“精度”的意思,但在C++中,除了定点计数法科学计数法以外它其实表示的是“有效位数”!在定点计数法科学计数法中,它表示的是小数点位数。

有效位数:有效数字的位数就是有效位数,总数字总位数。

精度:根据四舍五入的规则,一个数的精确位,要看这个数的最后一位,如 0.60 的最后一位是小数点后两位,那就是精确到百分位,有效位数是3个。

setf()函数是ios_base类的一个成员函数。由于这个类是ostream类的基类,因此可以使用cout对象来调用该函数。例如,要左对齐,可使用下面的调用:

ios_base::fmtflags old = cout.setf(ios::left, ios::adjustfield);

注意:old存储的是未设置前的状态,而不是setf()后的状态,因为它返回的是以前的设置!

要恢复以前的设置,可以这样做:

cout.setf(old, ios::adjustfield);
#include <iostream>
#include <cmath>
using namespace std;
int main() 
	// 设置:1.左对齐;2.显示正号“+”;3.显示尾部小数点;4.精度为3
	cout.setf(ios_base::left, ios_base::adjustfield);
	cout.setf(ios_base::showpos);
	cout.setf(ios_base::showpoint);
	cout.precision(3);
	// 使用科学计数法,并存储设置前的计数法设置(默认计数法)
	ios_base::fmtflags old = cout.setf(ios_base::scientific,ios_base::floatfield);
	cout << "【左对齐】,显示正号+,显示尾部小数点,精度为3,科学计数法:" << endl;
	for (int n = 1; n <= 41; n += 10) 
		cout.width(11);
		cout << n / 0.5 << "|";
		cout.width(13);
		cout << sqrt(double(n)) << "|\\n";
	
	// 改对齐方式为:内部对齐
	cout.setf(ios_base::internal, ios_base::adjustfield);
	// 恢复为之前计数法设置(默认计数法)
	cout.setf(old, ios_base::floatfield);
	cout << "【内部对齐】,显示正号+,显示尾部小数点,精度为3,默认计数法:" << endl;
	for (int n = 1; n <= 41; n += 10) 
		cout.width(11);
		cout << n / 0.5 << "|";
		cout.width(13);
		cout << sqrt(double(n)) << "|\\n";
	
	// 改对齐方式为:右对齐;改计数法为:定点计数法
	cout.setf(ios_base::right, ios_base::adjustfield);
	cout.setf(ios_base::fixed, ios_base::floatfield);
	cout << "【右对齐】,显示正号+,显示尾部小数点,精度为3,定点计数法:" << endl;
	for (int n = 1; n <= 41; n += 10) 
		cout.width(11);
		cout << n / 0.5 << "|";
		cout.width(13);
		cout << sqrt(double(n)) << "|\\n";
	
	return 0;
【左对齐】,显示正号+,显示尾部小数点,精度为3,科学计数法:
+2.000e+00 |+1.000e+00   |
+2.200e+01 |+3.317e+00   |
+4.200e+01 |+4.583e+00   |
+6.200e+01 |+5.568e+00   |
+8.200e+01 |+6.403e+00   |
【内部对齐】,显示正号+,显示尾部小数点,精度为3,默认计数法:
+      2.00|+        1.00|
+      22.0|+        3.32|
+      42.0|+        4.58|
+      62.0|+        5.57|
+      82.0|+        6.40|
【右对齐】,显示正号+,显示尾部小数点,精度为3,定点计数法:
     +2.000|       +1.000|
    +22.000|       +3.317|
    +42.000|       +4.583|
    +62.000|       +5.568|
    +82.000|       +6.403|

默认填充是空格,默认对齐是右对齐。

调用setf()的效果可以通过unsetf()消除,后者的原型如下: 

void unsetf(fmtflags mask);

其中,mask是位模式。mask中所有的位都设置为1,将使得对应的位被复位。也就是说,setf()将位设置为1,unsetf()将位恢复为0。

cout.setf(ios_base::showpoint);// 显示尾部的小数点
cout.unsetf(ios_base::showpoint);// 不显示尾部小数点
cout.setf(ios_base::boolalpha); // 显示true、false
cout.unsetf(ios_base::boolalpha);// 显示1、0

你可能注意到了,没有专门指示浮点数默认显示模式的标记。系统的工作原理如下:仅当只有定点位被设置时使用定点表示法;仅当只有科学位被设置时使用科学表示法;对于其他组合,如没有位被设置或两位都被设置时,将使用默认模式。

启用默认模式的两方法:

1. cout.setf(0, ios_base::floatfield);// go to default mode

2. cout.unset(ios_base::floatfield);// go to default mode

如果已知cout处于定点状态,则可以使用参数ios_base::fixed调用函数unsetf()来切换到默认模式;然而,无论cout的当前状态如何,使用参数ios_base::floatfield调用函数unsetf()都将切换到默认模式,因此这是一种更好的选择。

7.标准控制符

 使用setf()不是进行格式化的、对用户最为友好的方法,C++提供了多个控制符,能够调用setf(),并自动提供正确的参数。前面已经介绍过dec、hex和oct,这些控制符(多数都不适用于老式C++实现)的工作方式都与hex相似。例如,下面的语句打开左对齐和定点选项:

cout << left << fixed;

下表列出了这些控制符以及其他一些控制符:

8.头文件<iomanip>

使用iostream工具来设置一些格式值(如字段宽度)不太方便。为简化工作,C++在头文件<iomanip>中提供了其他一些控制符,它们能够提供前面讨论过的服务,但表示起来更方便。3个最常用的控制符分别是setprecision()、setfill()和setw(),它们分别用来设置精度、填充字符和字段宽度。与前面讨论的控制符不同的是,这3个控制符带参数。

/*unspecified*/ setprecision(int n);
/*unspecified*/ setfill(char_type c);
/*undefined*/ setw(int n);
/*unspecified*/ setbase(int base);

setprecision() 控制符接受一个指定精度的整数参数;
setfill() 控制符接受一个指定填充字符的char参数;
setw() 控制符接受一个指定字段宽度的整数参数;
setbase() 控制符接受一个指定的数字基数(如8,10,16)的整数参数。

由于它们都是控制符,因此可以用cout语句连接起来。这样,setw()控制符在显示多列值时尤其方便。下面程序清单演示了这一点,它对于每一行输出,都多次修改了字段宽度和填充字符,同时使用了一些较新的标准控制符。

#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main () 
	cout << fixed << right;// 定点表示法,右对齐
	cout << setw(6) << "N"
	     << setw(14) << "square root"
		 << setw(15) << "fourth root"
		 << endl;
	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

现在可以生成几乎完全对齐的列了。使用fixed控制符导致显示末尾的0。 

以上是关于❥关于C++之用cout进行格式化的主要内容,如果未能解决你的问题,请参考以下文章

PAT归纳总结——关于C++输入输出格式问题的一些总结

数据结构作业之用队列实现的基数排序(Java版)

c++,怎么控制输出格式,使每一行格式相同,且对齐,就比如输出一个表格形式

C++中使用cout输出int时,怎么在高位补0?如输出003.

C++ cout格式化输出(转)

C++ 嵌套 for 循环,用于设置基数/指数的指数