使用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”)。