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 );
。
绑定操作步骤
- 确定需要绑定的输入流和输出流。
-
使用
tie函数将输出流的地址传递给输入流的tie成员函数。 -
若要解除绑定,将
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';
-
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 任务。
超级会员免费看
1342

被折叠的 条评论
为什么被折叠?



