72、C++ 流输入输出深入解析

C++ 流输入输出深入解析

1. 流格式状态与流操纵符

1.1 整数流基数

C++ 提供了 dec hex oct 流操纵符,分别用于指定整数以十进制、十六进制和八进制值显示。若未使用这些操纵符,整数输出默认采用十进制。在流提取时,以 0 开头的整数被视为八进制值,以 0x 0X 开头的整数被视为十六进制值,其他整数则被视为十进制值。一旦为流指定了特定的基数,该流上的所有整数都将使用该基数进行处理,直到指定不同的基数或程序终止。

showbase 流操纵符会强制输出整数值的基数。十进制数默认输出,八进制数以 0 开头输出,十六进制数以 0x 0X 开头输出( uppercase 流操纵符可决定使用哪种形式)。若要重置 showbase 设置,可输出 noshowbase 流操纵符。

以下是一个示例代码:

#include <iostream>
using namespace std;

int main()
{
   int x = 100;
   cout << "Printing integers preceded by their base:" << endl 
        << x << endl; // 输出十进制值
   cout << oct << x << endl; // 输出八进制值
   cout << hex << x << endl; // 输出十六进制值
   return 0;
}

1.2 浮点数;科学记数法和定点表示法

scientific fixed 这两个粘性流操纵符可控制浮点数的输出格式。 scientific 流操纵符会强制浮点数以科学记数法格式输出, fixed 流操纵符则会强制浮点数输出小数点右侧指定数量的数字(由 precision 成员函数或 setprecision 流操纵符指定)。若不使用其他操纵符,浮点数的值将决定输出格式。

示例代码如下:

#include <iostream>
using namespace std;

int main()
{
   double x = 0.001234567;
   double y = 1.946e9;
   cout << "Displayed in default format:" << endl
        << x << '\t' << y << endl;
   cout << "\nDisplayed in scientific format:" << endl
        << scientific << x << '\t' << y << endl;
   cout << "\nDisplayed in fixed format:" << endl
        << fixed << x << '\t' << y << endl;
   return 0;
}

1.3 大小写控制

uppercase 流操纵符会分别在十六进制整数值或科学记数法浮点数中输出大写的 X E ,同时也会使十六进制值中的所有字母变为大写。默认情况下,十六进制值的字母和科学记数法浮点数的指数以小写形式出现。若要重置 uppercase 设置,可输出 nouppercase 流操纵符。

示例代码:

#include <iostream>
using namespace std;

int main()
{
   cout << "Printing uppercase letters in scientific" << endl
        << "notation exponents and hexadecimal values:" << endl;
   cout << uppercase << 4.345e10 << endl 
        << hex << showbase << 123456789 << endl;
   return 0;
}

1.4 指定布尔格式

C++ 提供了 bool 数据类型,其值可以为 false true ,这是比使用 0 表示 false 、非零表示 true 更优的选择。默认情况下, bool 变量输出为 0 1 。不过,我们可以使用 boolalpha 流操纵符将输出流设置为以字符串 "true" "false" 显示 bool 值,使用 noboolalpha 流操纵符则可将输出流设置为以整数形式显示 bool 值(即默认设置)。

示例代码:

#include <iostream>
using namespace std;

int main()
{
   bool booleanValue = true;
   cout << "booleanValue is " << booleanValue << endl;
   cout << "booleanValue (after using boolalpha) is "
        << boolalpha << booleanValue << endl << endl;
   cout << "switch booleanValue and use noboolalpha" << endl;
   booleanValue = false;
   cout << noboolalpha << endl;
   cout << "booleanValue is " << booleanValue << endl;
   cout << "booleanValue (after using boolalpha) is "
        << boolalpha << booleanValue << endl;
   return 0;
}

1.5 通过 flags 成员函数设置和重置格式状态

flags 成员函数在无参数时,会以 fmtflags 数据类型( ios_base 类的)返回当前的格式设置,该设置代表流的格式状态;在带有 fmtflags 参数时,会将格式状态设置为参数指定的值,并返回先前的状态设置。不同系统中 flags 返回值的初始设置可能有所不同。

以下是一个示例代码:

#include <iostream>
using namespace std;

int main()
{
   int integerValue = 1000;
   double doubleValue = 0.0947628;
   cout << "The value of the flags variable is: " << cout.flags()
        << "\nPrint int and double in original format:\n"
        << integerValue << '\t' << doubleValue << endl << endl;
   ios_base::fmtflags originalFormat = cout.flags();
   cout << showbase << oct << scientific;
   cout << "The value of the flags variable is: " << cout.flags()
        << "\nPrint int and double in a new format:\n"
        << integerValue << '\t' << doubleValue << endl << endl;
   cout.flags( originalFormat );
   cout << "The restored value of the flags variable is: " 
        << cout.flags()
        << "\nPrint values in original format again:\n"
        << integerValue << '\t' << doubleValue << endl;
   return 0;
}

2. 流错误状态

流的状态可以通过 ios_base 类中的位进行测试。 eofbit 会在输入流遇到文件结束符后被设置,程序可使用 eof 成员函数来确定在尝试从流中提取超出文件末尾的数据后是否遇到了文件结束符。 failbit 会在流出现格式错误且没有字符被输入时被设置(例如,尝试读取数字但用户输入了字符串),此时字符不会丢失, fail 成员函数可报告流操作是否失败,通常这种错误是可以恢复的。 badbit 会在出现导致数据丢失的错误时被设置, bad 成员函数可报告流操作是否失败,一般这种严重的失败是不可恢复的。 goodbit 会在流的 eofbit failbit badbit 均未被设置时被设置, good 成员函数在 bad fail eof 函数都返回 false 时返回 true ,I/O 操作应仅在 “良好” 的流上执行。 rdstate 成员函数会返回流的错误状态, clear 成员函数用于将流的状态恢复为 “良好”,以便继续进行 I/O 操作。

以下是一个测试流错误状态的示例代码:

#include <iostream>
using namespace std;

int main()
{
   int integerValue;
   cout << "Before a bad input operation:"
        << "\ncin.rdstate(): " << cin.rdstate()
        << "\n    cin.eof(): " << cin.eof()
        << "\n   cin.fail(): " << cin.fail()
        << "\n    cin.bad(): " << cin.bad()
        << "\n   cin.good(): " << cin.good()
        << "\n\nExpects an integer, but enter a character: ";
   cin >> integerValue;
   cout << endl;
   cout << "After a bad input operation:"
        << "\ncin.rdstate(): " << cin.rdstate()
        << "\n    cin.eof(): " << cin.eof()
        << "\n   cin.fail(): " << cin.fail()
        << "\n    cin.bad(): " << cin.bad()
        << "\n   cin.good(): " << cin.good() << endl << endl;
   cin.clear();
   cout << "After cin.clear()" << "\ncin.fail(): " << cin.fail()
        << "\ncin.good(): " << cin.good() << endl;
   return 0;
}

流错误状态检查流程

graph TD;
    A[开始] --> B[检查流状态];
    B --> C{是否遇到EOF};
    C -- 是 --> D[eofbit置位];
    C -- 否 --> E{是否格式错误};
    E -- 是 --> F[failbit置位];
    E -- 否 --> G{是否数据丢失错误};
    G -- 是 --> H[badbit置位];
    G -- 否 --> I[goodbit置位];
    D --> J[使用eof函数检查];
    F --> K[使用fail函数检查];
    H --> L[使用bad函数检查];
    I --> M[使用good函数检查];
    J --> N[根据结果处理];
    K --> N;
    L --> N;
    M --> N;
    N --> O[结束];

3. 绑定输出流和输入流

在交互式应用中,通常会涉及用于输入的 istream 和用于输出的 ostream 。当屏幕上出现提示消息时,用户会输入相应的数据。显然,提示消息需要在输入操作之前显示。由于存在输出缓冲,输出只有在缓冲区填满、程序显式刷新输出或程序结束时才会显示。C++ 提供了 tie 成员函数来同步 istream ostream 的操作,以确保输出在后续输入之前显示。例如, cin.tie( &cout ); 会将 cout ostream )与 cin istream )绑定。实际上,C++ 会自动执行此操作以创建用户的标准输入/输出环境,但用户也可以显式绑定其他 istream / ostream 对。若要解除输入流与输出流的绑定,可使用 inputStream.tie( 0 );

绑定操作步骤

  1. 确定需要绑定的输入流和输出流。
  2. 使用 tie 函数将输出流的地址传递给输入流的 tie 成员函数。
  3. 若要解除绑定,将 0 传递给输入流的 tie 成员函数。

4. 总结

本文详细介绍了 C++ 流输入输出的多个方面,包括流格式状态、流操纵符、流错误状态以及绑定输出流和输入流等内容。通过使用各种流操纵符,我们可以灵活控制整数、浮点数、布尔值等数据类型的输出格式。同时,了解流错误状态的检查和处理方法,有助于编写健壮的 I/O 程序。绑定输出流和输入流的操作则能确保交互式应用中输出和输入的正确顺序。希望这些内容能帮助你更好地掌握 C++ 流输入输出的相关知识。

流操纵符总结

流操纵符 功能
dec 指定整数以十进制输出
hex 指定整数以十六进制输出
oct 指定整数以八进制输出
showbase 强制输出整数值的基数
scientific 强制浮点数以科学记数法输出
fixed 强制浮点数按指定精度输出
uppercase 输出大写的 X E
boolalpha 以字符串形式输出 bool
noboolalpha 以整数形式输出 bool

流错误状态检查函数总结

函数 功能
eof() 检查是否遇到文件结束符
fail() 检查是否格式错误
bad() 检查是否数据丢失错误
good() 检查流是否正常
rdstate() 返回流的错误状态
clear() 恢复流的状态为 “良好”

5. 自我评测练习

5.1 填空题

以下是一些填空题,帮助你巩固所学知识:
1. C++ 中的输入/输出以 _的形式进行。
2. 用于对齐的流操纵符是
_和
3. 可以使用成员函数 _来设置和重置格式状态。
4. 大多数进行 I/O 操作的 C++ 程序应包含
头文件,其中包含所有流 I/O 操作所需的声明。
5. 使用参数化操纵符时,必须包含 _头文件。
6.
_头文件包含文件处理所需的声明。
7. ostream 成员函数 _用于执行无格式输出。
8. 输入操作由
_类支持。
9. 标准错误流输出被定向到流对象 _或

10. 输出操作由 _类支持。
11. 流插入运算符的符号是

12. 与系统上的标准设备对应的四个对象包括 _、
_和
13. 流提取运算符的符号是 _。
14. 流操纵符
_和 _分别指定整数应以八进制、十六进制和十进制格式显示。
15. ____流操纵符使正数显示时带有加号。

5.2 判断题

判断以下陈述的对错,并解释错误原因:
1. 带有 long 参数的 flags 流成员函数会将 flags 状态变量设置为其参数,并返回其先前的值。
2. 流插入运算符 << 和流提取运算符 >> 已重载,可处理所有标准数据类型(包括字符串和内存地址,仅流插入)以及所有用户定义的数据类型。
3. 无参数的 flags 流成员函数会重置流的格式状态。
4. 流提取运算符 >> 可以通过一个运算符函数进行重载,该函数接受一个 istream 引用和一个用户定义类型的引用作为参数,并返回一个 istream 引用。
5. 流插入运算符 << 可以通过一个运算符函数进行重载,该函数接受一个 istream 引用和一个用户定义类型的引用作为参数,并返回一个 istream 引用。
6. 默认情况下,使用流提取运算符 >> 进行输入时,总是会跳过输入流中的前导空白字符。
7. rdstate 流成员函数返回流的当前状态。
8. cout 流通常连接到显示屏。
9. 如果 bad fail eof 成员函数都返回 false ,则 good 流成员函数返回 true
10. cin 流通常连接到显示屏。
11. 如果在流操作期间发生不可恢复的错误, bad 成员函数将返回 true
12. 输出到 cerr 是无缓冲的,而输出到 clog 是有缓冲的。
13. showpoint 流操纵符会强制浮点数以默认的六位精度输出,除非精度值已更改,此时浮点数将以指定的精度输出。
14. ostream 成员函数 put 输出指定数量的字符。
15. 流操纵符 dec oct hex 仅影响下一个整数输出操作。

5.3 编写 C++ 语句

为以下每个任务编写一条单独的语句:
1. 输出字符串 "Enter your name: "
2. 使用流操纵符使科学记数法中的指数和十六进制值中的字母以大写形式输出。
3. 输出 char * 类型变量 myString 的地址。
4. 使用流操纵符确保浮点数以科学记数法输出。
5. 输出 int * 类型变量 integerPtr 中的地址。
6. 使用流操纵符,使整数输出时显示八进制和十六进制值的整数基数。
7. 输出 float * 类型指针 floatPtr 所指向的值。
8. 使用流成员函数将填充字符设置为 * ,用于在比输出值大的字段宽度中打印。使用流操纵符重复此语句。
9. 使用 ostream 函数 put 在一条语句中输出字符 'O' 'K'
10. 获取下一个输入字符的值,而不将其从流中提取出来。
11. 使用 istream 成员函数 get 以两种不同的方式将单个字符输入到 char 类型变量 charValue 中。
12. 输入并丢弃输入流中的接下来六个字符。
13. 使用 istream 成员函数 read 将 50 个字符输入到 char 数组 line 中。
14. 读取 10 个字符到字符数组 name 中。如果遇到分隔符 . ,则停止读取字符。不要将分隔符从输入流中移除。再写一条语句执行此任务,并将分隔符从输入中移除。
15. 使用 istream 成员函数 gcount 确定上一次调用 istream 成员函数 read 输入到字符数组 line 中的字符数,并使用 ostream 成员函数 write 输出该数量的字符。
16. 输出 124、18.376、 'Z' 、1000000 和 "String" ,用空格分隔。
17. 显示 cout 的当前精度设置。
18. 将整数值输入到 int 变量 months 中,将浮点数值输入到 float 变量 percentageRate 中。
19. 使用流操纵符以 3 位精度输出 1.92、1.925 和 1.9258,用制表符分隔。
20. 使用流操纵符以八进制、十六进制和十进制输出整数 100,用制表符分隔。
21. 使用流操纵符更改基数,以十进制、八进制和十六进制输出整数 100,用制表符分隔。
22. 将 1234 右对齐输出到 10 位字段中。
23. 读取字符到字符数组 line 中,直到遇到字符 'z' ,最多读取 20 个字符(包括终止空字符)。不要从流中提取分隔符字符。
24. 使用整数变量 x y 指定用于显示双精度值 87.4573 的字段宽度和精度,并显示该值。

5.4 查找并纠正代码错误

识别以下每个语句中的错误,并解释如何纠正:
1. cout << "Value of x <= y is: " << x <= y;
2. 以下语句应打印字符 'c' 的整数值。

cout << 'c';
  1. cout << ""A string in quotes"";

5.5 显示输出

对于以下每个代码段,显示其输出:
1.

cout << "12345" << endl;
cout.width( 5 );
cout.fill( '*' );
cout << 123 << endl << 123;
cout << setw( 10 ) << setfill( '$' ) << 10000;
cout << setw( 8 ) << setprecision( 3 ) << 1024.987654;
cout << showbase << oct << 99 << endl << hex << 99;
cout << 100000 << endl << showpos << 100000;
cout << setw( 10 ) << setprecision( 2 ) << scientific << 444.93738;

6. 练习题

6.1 编写 C++ 语句

为以下每个任务编写一条语句:
1. 将整数 40000 左对齐输出到 15 位字段中。
2. 将字符串读入字符数组变量 state 中。
3. 分别带符号和不带符号输出 200。
4. 以十六进制形式输出十进制值 100,并在前面加上 0x
5. 读取字符到数组 charArray 中,直到遇到字符 'p' ,最多读取 10 个字符(包括终止空字符)。从输入流中提取分隔符并丢弃它。
6. 在 9 位字段中以前导零输出 1.234。

6.2 输入十进制、八进制和十六进制值

编写一个程序,测试以十进制、八进制和十六进制格式输入整数值。程序读取的每个整数都以这三种格式输出。使用以下输入数据测试程序:10、010、0x10。

6.3 以整数形式打印指针值

编写一个程序,使用强制类型转换将指针值以所有整数数据类型打印出来。哪些类型会打印出奇怪的值?哪些类型会导致错误?

6.4 使用字段宽度打印

编写一个程序,测试在不同大小的字段中打印整数值 12345 和浮点数值 1.2345 的结果。当值在比其本身小的字段中打印时会发生什么?

6.5 四舍五入

编写一个程序,将值 100.453627 四舍五入到最接近的个位、十分位、百分位、千分位和万分位并输出。

6.6 字符串长度

编写一个程序,从键盘输入一个字符串,并确定该字符串的长度。在宽度为字符串长度两倍的字段中打印该字符串。

6.7 华氏温度转换为摄氏温度

编写一个程序,将 0 到 212 度的整数华氏温度转换为精确到 3 位小数的浮点摄氏温度。使用公式 celsius = 5.0 / 9.0 * ( fahrenheit - 32 ); 进行计算。输出应打印在两个右对齐的列中,并且摄氏温度应在正负值前都带有符号。

6.8 读取带引号的字符串

在某些编程语言中,字符串可以用单引号或双引号括起来输入。编写一个程序,读取三个字符串 suzy "suzy" 'suzy' 。单引号和双引号是被忽略还是作为字符串的一部分读取?

6.9 重载流提取运算符读取电话号码

重新实现流提取运算符,对输入进行以下错误检查:
1. 将整个电话号码输入到一个数组中。测试是否输入了正确数量的字符。对于 (800) 555-1212 形式的电话号码,总共应读取 14 个字符。使用 ios_base 成员函数 clear 为不正确的输入设置 failbit
2. 区号和交换码不以 0 或 1 开头。测试电话号码的区号和交换码部分的第一个数字,确保它们都不以 0 或 1 开头。使用 ios_base 成员函数 clear 为不正确的输入设置 failbit
3. 区号的中间数字过去限制为 0 或 1(尽管现在已经改变)。测试中间数字是否为 0 或 1。使用 ios_base 成员函数 clear 为不正确的输入设置 failbit
如果以上操作都没有为不正确的输入设置 failbit ,则将电话号码的各个部分复制到 PhoneNumber 对象的 areaCode exchange line 成员中。如果输入时 failbit 已被设置,程序应打印错误消息并结束,而不是打印电话号码。

6.10 点类

编写一个程序,完成以下任务:
1. 创建一个用户定义的 Point 类,其中包含私有整数数据成员 xCoordinate yCoordinate ,并将流插入和流提取重载运算符函数声明为该类的友元。
2. 定义流插入和流提取运算符函数。流提取运算符函数应确定输入的数据是否有效,如果无效,则设置 failbit 以指示输入不正确。流插入运算符不应在输入错误发生后显示该点。
3. 编写一个 main 函数,使用重载的流提取和流插入运算符测试用户定义的 Point 类的输入和输出。

6.11 复数类

编写一个程序,完成以下任务:
1. 创建一个用户定义的 Complex 类,其中包含私有整数数据成员 real imaginary ,并将流插入和流提取重载运算符函数声明为该类的友元。
2. 定义流插入和流提取运算符函数。流提取运算符函数应确定输入的数据是否有效,如果无效,则设置 failbit 以指示输入不正确。输入应采用 3 + 8i 的形式。
3. 值可以为正或负,并且可能只提供其中一个值,在这种情况下,相应的数据成员应设置为 0。流插入运算符不应在输入错误发生后显示该点。对于负虚数值,应打印减号而不是加号。
4. 编写一个 main 函数,使用重载的流提取和流插入运算符测试用户定义的 Complex 类的输入和输出。

6.12 打印 ASCII 值表

编写一个程序,使用 for 语句打印 ASCII 字符集中从 33 到 126 的字符的 ASCII 值表。程序应打印每个字符的十进制值、八进制值、十六进制值和字符值。使用流操纵符 dec oct hex 打印整数值。

6.13 字符串终止空字符

编写一个程序,展示 istream 成员函数 getline 和三参数 get 都以字符串终止空字符结束输入字符串。此外,展示 get 会将分隔符字符留在输入流中,而 getline 会提取分隔符字符并丢弃它。流中未读取的字符会怎样?

7. 总结回顾

通过本文的学习,我们深入了解了 C++ 流输入输出的各个方面。从流格式状态和流操纵符的使用,到流错误状态的检查和处理,再到绑定输出流和输入流以确保交互式应用的正确顺序,以及通过大量的练习题巩固所学知识,我们对 C++ 流输入输出有了更全面的认识。

关键知识点总结

知识点 描述
流操纵符 用于控制整数、浮点数、布尔值等数据类型的输出格式,如 dec hex oct scientific fixed
流错误状态 通过 eofbit failbit badbit goodbit 来表示流的不同状态,可使用 eof fail bad good 函数进行检查
绑定流 使用 tie 函数绑定输入流和输出流,确保输出在输入之前显示

希望这些内容能帮助你在实际编程中更好地运用 C++ 流输入输出,编写出更高效、健壮的程序。在后续的学习和实践中,不断巩固和拓展这些知识,你将能够处理更复杂的 I/O 任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值