C++基础教程面向对象(学习笔记(103))

使用ostream和ios输出

在本节中,我们将介绍iostream输出类(ostream)的各个方面。

注意:本课程中的所有I / O功能都位于std命名空间中。这意味着所有I / O对象和函数都必须以“std ::”为前缀,或者必须使用“using namespace std;”语句。

插入操作符

插入运算符(<<)用于将信息放入输出流。C ++为所有内置数据类型预定义了插入操作,您已经了解了如何为自己的类重载插入操作符。

在关于流的课程中,您看到istream和ostream都来自一个名为ios的类。ios(和ios_base)的一个工作是控制输出的格式化选项。

格式化

有两种方法可以更改格式选项:标志和操纵器。您可以将标志视为可以打开和关闭的布尔变量。 操纵器是放置在流中的对象,它影响输入和输出的方式。

要打开标志,请使用setf()函数,并将相应的标志作为参数。例如,默认情况下,C ++不会在正数前面打印+号。但是,通过使用std :: showpos标志,我们可以更改此行为:

std::cout.setf(std::showpos); // 打开std :: showpos标志
std::cout << 27 << '\n';

这导致以下输出:

+27
可以使用OR(|)运算符一次打开多个ios标志:

std::cout.setf(std::showpos | std::uppercase); //打开std :: showpos和std :: uppercase标志
std::cout << 27 << '\n';

要关闭标志,请使用unsetf()函数:

std::cout.setf(std::showpos); //打开std :: showpos标志
std::cout << 27 << '\n';
std::cout.unsetf(std::showpos); //关闭std :: showpos标志
std::cout << 28 << '\n';

这导致以下输出:

+27
28
当使用需要提及的setf()时,还有一点棘手。许多标志属于组,称为格式组。格式组是一组执行类似的(有时相互排斥)格式化选项标志。例如,名为“basefield”的格式组包含标志“oct”,“dec”和“hex”,它们控制整数值的基数。默认情况下,设置“dec”标志。因此,如果我们这样做:

std::cout.setf(std::hex); // 尝试打开十六进制输出
std::cout << 27 << '\n';

我们得到以下输出:

27
它没用!原因是因为setf()只打开了标志 - 它不够聪明,无法关闭互斥标志。因此,当我们打开std :: hex时,std :: dec仍然打开,并且std :: dec显然优先。有两种方法可以解决这个问题。

首先,我们可以关闭std :: dec,以便只设置std :: hex:

std::cout.unsetf(std::dec); //关闭十进制输出
std::cout.setf(std::hex); // 打开十六进制输出
std::cout << 27 << '\n';

现在我们按预期获得输出:

1b
第二种方法是使用另一种形式的setf(),它接受两个参数:第一个参数是要设置的标志,第二个参数是它所属的格式化组。使用这种形式的setf()时,属于该组的所有标志都将被关闭,并且只有传入的标志才会打开。例如:

// 打开std :: hex作为唯一的std :: basefield标志
std::cout.setf(std::hex, std::basefield);
std::cout << 27 << '\n';

这也产生了预期的输出:

1b
使用setf()和unsetf()往往很尴尬,因此C ++提供了另一种更改格式选项的方法:操纵器。关于操纵器的好处是它们足够聪明,可以打开和关闭相应的标志。以下是使用一些操纵器来更改基础的示例:

std::cout << std::hex << 27 << '\n'; // 以十六进制打印27
std::cout << 28 << '\n'; // 我们还是十六进制
std::cout << std::dec << 29 << '\n'; // 回到小数

该程序产生输出:

1b
1c
29
通常,使用操纵器比设置和取消设置标志要容易得多。通过标志和操纵器可以获得许多选项(例如更改基础),但是,其他选项只能通过标记或操纵器获得,因此了解如何使用它们非常重要。

有用的格式化程序

这是一些更有用的标志,操纵器和成员函数的列表。标志存在于ios类中,操纵符存在于std命名空间中,成员函数存在于ostream类中。

Group Flag 含义
boolalpha 如果设置,则布尔值打印“true”或“false”。如果未设置,则布尔值打印0或1
Manipulator 含义
boolalpha 布尔打印“真实”或“虚假”
noboolalpha 布尔值打印0或1(默认)

例:

std::cout << true << " " << false << '\n';
 
std::cout.setf(std::boolalpha);
std::cout << true << " " << false << '\n';
 
std::cout << noboolalpha << true << " " << false << '\n';
 
std::cout << boolalpha << true << " " << false << '\n';

结果:

1 0
true false
1 0
true false

Group Flag 含义
showpos 如果设置,则使用+前缀正数
Manipulator 含义
showpos 使用+前缀正数
noshowpos 不使用+前缀正数

例:

std::cout << 5 << '\n';
 
std::cout.setf(std::showpos);
std::cout << 5 << '\n';
 
std::cout << noshowpos << 5 << '\n';
 
std::cout << showpos << 5 << '\n';

结果:

5
+5
5
+5

Group Flag 含义
大写 如果设置,则使用大写字母
Manipulator 含义
大写 使用大写字母
小写 使用小写字母

例:

std::cout << 12345678.9 << '\n';
 
std::cout.setf(std::uppercase);
std::cout << 12345678.9 << '\n';
 
std::cout << nouppercase << 12345678.9 << '\n';
 
std::cout << uppercase << 12345678.9 << '\n';

结果:

1.23457e + 007
1.23457E + 007
1.23457e + 007
1.23457E + 007

Group Flag 含义
basefield dec 以十进制打印值(默认)
basefield hex 以十六进制打印值
basefield oct 以八进制打印值
basefield (none) 根据值的前导字符打印值
Manipulator 含义
dec 以十进制打印值
hex 以十六进制打印值
oct 以八进制打印值

例:

std::cout << 27 << '\n';
 
std::cout.setf(std::dec, std::basefield);
std::cout << 27 << '\n';
 
std::cout.setf(std::oct, std::basefield);
std::cout << 27 << '\n';
 
std::cout.setf(std::hex, std::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
到现在为止,您应该能够看到通过标志和操纵器设置格式之间的关系。在将来的示例中,我们将使用操纵器,除非它们不可用。

精度,符号和小数点

使用操纵器(或标志),可以更改显示浮点数的精度和格式。有几种格式化选项以复杂的方式组合,因此我们将仔细研究这一点。

Group Flag 含义
floatfield fixed 对浮点数使用十进制表示法
floatfield scientific 对浮点数使用科学记数法
floatfield (none) 固定用于少数位的数字,否则为科学
floatfield showpoint 始终显示小数点,并为浮点值尾随0
Manipulator 含义
fixed 对值使用十进制表示法
scientific 使用科学记数法表示值
showpoint 显示小数点,并为浮点值尾随0
noshowpoint 对于浮点值,不显示小数点和尾随0
setprecision(int) 设置浮点数的精度(在iomanip.h中定义)
成员函数 含义
precision()返回浮点数的当前精度
sprecision(int) 使设置浮点数的精度并返回旧精度

如果使用固定或科学记数法,精度将确定显示分数中的小数位数。请注意,如果精度小于有效位数,则数字将被舍入。

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
这是一个包含更多示例的汇总表:
在这里插入图片描述
宽度,填充字符和对齐方式

通常,当您打印数字时,会打印数字而不考虑它们周围的空间。但是,可以向左或向右对齐数字的打印。为此,我们必须首先定义一个字段宽度,它定义一个值将具有的输出空间的数量。如果打印的实际数字小于字段宽度,则将左对齐或右对齐(如指定的那样)。如果实际数字大于字段宽度,则不会被截断 - 它将溢出字段。

Group Flag 含义
adjustfield internal 左对齐数字的符号,并对该值进行右对齐
adjustfield left 以左对齐符号和值
adjustfield right 右对齐符号和值(默认)
Manipulator 含义
internal 左对齐数字的符号,并对该值进行右对齐
left 左对齐符号和值
right 右对齐符号和值(默认)
setfill(char) 将参数设置为填充字符(在iomanip.h中定义)
setw(int) 将输入和输出的字段宽度设置为参数(在iomanip.h中定义)
成员函数 含义
fill() 返回当前填充字符
fill(char) 设置填充字符并返回旧填充字符
width() 返回当前字段宽度
width(int) 设置当前字段宽度并返回旧字段宽度

为了使用任何这些格式化程序,我们首先必须设置字段宽度。这可以通过width(int)成员函数或setw()操纵器来完成。请注意,右对齐是默认值。

std::cout << -12345 << '\n'; //打印默认值,没有字段宽度
std::cout << std::setw(10) << -12345 << '\n'; // 打印默认字段宽度
std::cout << std::setw(10) << left << -12345 << '\n'; // 打印左对齐
std::cout << std::setw(10) << right << -12345 << '\n'; // 打印右对齐
std::cout << std::setw(10) << internal << -12345 << '\n'; // 打印内部合理

这会产生结果:

-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) << left << -12345 << '\n'; // print left justified
std::cout << std::setw(10) << right << -12345 << '\n'; // print right justified
std::cout << std::setw(10) << internal << -12345 << '\n'; // print internally justified

这会产生输出:

-12345
**** - 12345
-12345 ****
**** - 12345

  • **** 12345
    请注意,字段中的所有空格都填充了填充字符。

ostream类和iostream库包含其他可能有用的输出函数,标志和操作符,具体取决于您需要执行的操作。与istream类一样,这些主题更适合专注于标准库的教程或书籍(例如Nicolai M. Josuttis出版的优秀书籍“The C ++ Standard Template Library”)。

猜你喜欢

转载自blog.csdn.net/qq_41879485/article/details/84919843