一、Output with ostream and ios
istream 和 ostream 都派生自一个名为 ios 的类。 ios(和 ios_base)的工作之一是控制输出的格式化选项。
有两种方法可以更改格式选项:标志和操纵器。 您可以将标志视为可以打开和关闭的布尔变量。 操纵器是放置在流中的对象,它们影响事物的输入和输出方式。
1、格式化
要打开标志,请使用 setf() 函数,并将适当的标志作为参数。 例如,默认情况下,C++ 不会在正数前面打印 + 号。 但是,通过使用 std::ios::showpos 标志,我们可以改变这种行为:
std::cout.setf(std::ios::showpos); // turn on the std::ios::showpos flag
std::cout << 27 << '\n';
//输出
+27
可以使用按位或 (|) 运算符一次打开多个 ios 标志:
std::cout.setf(std::ios::showpos | std::ios::uppercase); // turn on the std::ios::showpos and std::ios::uppercase flag
std::cout << 1234567.89f << '\n';
//输出
+1.23457E+06
要关闭标志,请使用 unsetf() 函数:
std::cout.setf(std::ios::showpos); // turn on the std::ios::showpos flag
std::cout << 27 << '\n';
std::cout.unsetf(std::ios::showpos); // turn off the std::ios::showpos flag
std::cout << 28 << '\n';
//输出
+27
28
使用 setf() 时还有一点需要提到的技巧。 许多标志属于组,称为格式组。 格式组是一组执行类似(有时互斥)格式选项的标志。 例如,名为“basefield”的格式组包含标志“oct”、“dec”和“hex”,它们控制整数值的基数。 默认情况下,设置了“dec”标志。 因此,如果我们这样做:
std::cout.setf(std::ios::hex); // try to turn on hex output
std::cout << 27 << '\n';
//输出
27
它没有用! 原因是因为 setf() 只打开标志 - 关闭互斥标志还不够聪明。 因此,当我们打开 std::hex 时,std::ios::dec 仍然打开,并且 std::ios::dec 显然优先。 有两种方法可以解决这个问题。
首先,我们可以关闭 std::ios::dec 以便只设置 std::hex :
std::cout.unsetf(std::ios::dec); // turn off decimal output
std::cout.setf(std::ios::hex); // turn on hexadecimal output
std::cout << 27 << '\n';
//输出
1b
第二种方法是使用不同形式的 setf(),它接受两个参数:第一个参数是要设置的标志,第二个是它所属的格式化组。 当使用这种形式的 setf() 时,所有属于该组的标志都被关闭,只有传入的标志被打开。 例如:
std::cout.setf(std::ios::hex, std::ios::basefield);
std::cout << 27 << '\n';
//输出
1b
使用 setf() 和 unsetf() 往往很尴尬,因此 C++ 提供了第二种更改格式选项的方法:操纵器。 操纵器的好处是它们足够聪明,可以打开和关闭适当的标志。 下面是一个使用一些操纵器来改变基础的例子:
std::cout << std::hex << 27 << '\n'; // print 27 in hex
std::cout << 28 << '\n'; // we're still in hex
std::cout << std::dec << 29 << '\n'; // back to decimal
//输出
1b
1c
29
一般来说,使用操纵器比设置和取消设置标志要容易得多。 许多选项都可以通过标志和操纵器(例如更改基础)使用,但是,其他选项只能通过标志或操纵器使用,因此了解如何使用两者很重要。
2、更多的标志
(1)boolalpha
代码参考
std::cout << true << " " << false << '\n';
std::cout.setf(std::ios::boolalpha);
std::cout << true << " " << false << '\n';
std::cout << std::noboolalpha << true << " " << false << '\n';
std::cout << std::boolalpha << true << " " << false << '\n';
//输出
1 0
true false
1 0
true false
(2)showpos
参考代码
std::cout << 5 << '\n';
std::cout.setf(std::ios::showpos);
std::cout << 5 << '\n';
std::cout << std::noshowpos << 5 << '\n';
std::cout << std::showpos << 5 << '\n';
//输出
5
+5
5
+5
(3)uppercase
参考代码
std::cout << 12345678.9 << '\n';
std::cout.setf(std::ios::uppercase);
std::cout << 12345678.9 << '\n';
std::cout << std::nouppercase << 12345678.9 << '\n';
std::cout << std::uppercase << 12345678.9 << '\n';
//输出
1.23457e+007
1.23457E+007
1.23457e+007
1.23457E+007
(4)显示不同进制
std::cout << 27 << '\n';
std::cout.setf(std::ios::dec, std::ios::basefield);
std::cout << 27 << '\n';
std::cout.setf(std::ios::oct, std::ios::basefield);
std::cout << 27 << '\n';
std::cout.setf(std::ios::hex, std::ios::basefield);
std::cout << 27 << '\n';
std::cout << std::dec << 27 << '\n';
std::cout << std::oct << 27 << '\n';
std::cout << std::hex << 27 << '\n';
//输出
27
27
33
1b
27
33
1b
(5)精度、符号和小数点
使用操纵器(或标志),可以更改显示浮点数的精度和格式。 有几种格式选项以某种复杂的方式组合在一起,因此我们将仔细研究一下。
如果使用固定计数法或科学计数法,精度决定了分数中显示的小数位数。 请注意,如果精度小于有效位数,则该数字将被四舍五入。
std::cout << std::fixed << '\n';
std::cout << std::setprecision(3) << 123.456 << '\n';
std::cout << std::setprecision(4) << 123.456 << '\n';
std::cout << std::setprecision(5) << 123.456 << '\n';
std::cout << std::setprecision(6) << 123.456 << '\n';
std::cout << std::setprecision(7) << 123.456 << '\n';
std::cout << std::scientific << '\n';
std::cout << std::setprecision(3) << 123.456 << '\n';
std::cout << std::setprecision(4) << 123.456 << '\n';
std::cout << std::setprecision(5) << 123.456 << '\n';
std::cout << std::setprecision(6) << 123.456 << '\n';
std::cout << std::setprecision(7) << 123.456 << '\n';
//输出
123.456
123.4560
123.45600
123.456000
123.4560000
1.235e+002
1.2346e+002
1.23456e+002
1.234560e+002
1.2345600e+002
如果既没有使用固定的也没有使用科学的,精度决定了应该显示多少有效数字。 同样,如果精度小于有效位数,则该数字将被四舍五入。
std::cout << std::setprecision(3) << 123.456 << '\n';
std::cout << std::setprecision(4) << 123.456 << '\n';
std::cout << std::setprecision(5) << 123.456 << '\n';
std::cout << std::setprecision(6) << 123.456 << '\n';
std::cout << std::setprecision(7) << 123.456 << '\n';
//输出
123
123.5
123.46
123.456
123.456
使用 showpoint 操纵器或标志,您可以使流写入小数点和尾随零。
std::cout << std::showpoint << '\n';
std::cout << std::setprecision(3) << 123.456 << '\n';
std::cout << std::setprecision(4) << 123.456 << '\n';
std::cout << std::setprecision(5) << 123.456 << '\n';
std::cout << std::setprecision(6) << 123.456 << '\n';
std::cout << std::setprecision(7) << 123.456 << '\n';
//输出
123.
123.5
123.46
123.456
123.4560
这是一个包含更多示例的汇总表:
(6)宽度、填充字符和对齐
通常,当您打印数字时,将打印数字而不考虑它们周围的空间。 但是,可以左对齐或右对齐数字的打印。 为了做到这一点,我们必须首先定义一个字段宽度,它定义了一个值将具有的输出空间的数量。 如果实际打印的数字小于字段宽度,它将左对齐或右对齐(如指定)。 如果实际数字大于字段宽度,则不会被截断——它会溢出字段。
为了使用这些格式化程序中的任何一个,我们首先必须设置一个字段宽度。 这可以通过 width(int) 成员函数或 setw() 操纵器来完成。 请注意,右对齐是默认设置。
std::cout << -12345 << '\n'; // print default value with no field width
std::cout << std::setw(10) << -12345 << '\n'; // print default with field width
std::cout << std::setw(10) << std::left << -12345 << '\n'; // print left justified
std::cout << std::setw(10) << std::right << -12345 << '\n'; // print right justified
std::cout << std::setw(10) << std::internal << -12345 << '\n'; // print internally justified
//输出
-12345
-12345
-12345
-12345
- 12345
需要注意的一点是 setw() 和 width() 只影响下一个输出语句。 它们不像其他一些标志/操纵器那样持久。
现在,让我们设置一个填充字符并执行相同的示例:
std::cout.fill('*');
std::cout << -12345 << '\n'; // print default value with no field width
std::cout << std::setw(10) << -12345 << '\n'; // print default with field width
std::cout << std::setw(10) << std::left << -12345 << '\n'; // print left justified
std::cout << std::setw(10) << std::right << -12345 << '\n'; // print right justified
std::cout << std::setw(10) << std::internal << -12345 << '\n'; // print internally justified
//输出
-12345
****-12345
-12345****
****-12345
-****12345
请注意,该字段中的所有空格都已用填充字符填充。
ostream 类和 iostream 库包含其他可能有用的输出函数、标志和操纵器,具体取决于您需要执行的操作。 。