C++学习-入门到精通【11】输入/输出流的深入剖析
C++学习-入门到精通【11】输入/输出流的深入剖析
目录
- C++学习-入门到精通【11】输入/输出流的深入剖析
- 一、流
- 1.传统流和标准流
- 2.iostream库的头文件
- 3.输入/输出流的类的对象
- 二、输出流
- 1.char* 变量的输出
- 2.使用成员函数put进行字符输出
- 三、输入流
- 1.get和getline成员函数
- 2.istream的成员函数peek、putback和ignore
- 3.类型安全
- 四、使用read、write和gcount的非格式化的I/O
- 五、流操纵符简介
- 1.整数流的基数:dec、oct、hex和setbase
- 2.浮点精度
- 3.位宽(width和setw)
- 4.用户自定义输入流操纵符
- 六、流的格式状态和流操纵符
- 1.尾数零和小数点(showpoint)
- 2.对齐(left、right和internal)
- 3.内容填充(fill和setfill)
- 4.整数流的基数
- 5.浮点数、科学计数法和定点小数记数法(scientific、fixed)
- 6.大写/小写控制(uppercase)
- 7.指定布尔类型(boolalpha)
- 8.通过成员函数flags设置和重置格式状态
- 七、流的错误状态
一、流
C++的I/O是以一连串的字节流的方式进行的。在输入操作中,字节从设备(例如,键盘、磁盘驱动器、网络连接等)流向内存。在输出操作中,字节从内存流向设备(例如,显示屏、打印机、磁盘驱动器、网络连接等)。入:表示进入内存;出:表示离开内存。以内存为参照物。
这里的流是一个抽象的概念,表示一个连续的数据序列。对于上面的不同设备而言,它们处理的数据格式其实并不相同,所以我们使用一个流的抽象概念将数据的生产者和消费者解耦(生产者只需将生成的数据给到流,之后输出到各种设备的不同操作由流来决定;反之亦然。)这就使得程序只需关注数据本身,不需要关注到数据如何流动到不同设备之中。
应用程序通过字节传达信息。字节可以组成字符、原始数据、图形图像、数字语音、数字视频或者任何应用程序所需要的其他信息。系统I/O结构应该能持续可靠地将字节从设备传输到内存,反之亦然。这种传输在设备中往往包含一些机械运动,例如,磁盘的旋转,或键盘的敲击。数据传输所花费的时间远远大于处理器内部处理数据所需要的时间。所以I/O操作需要仔细计划和协调,以保证最优的性能。
C++中同时提供低层次的
和高层次的
I/O功能。
低层次的I/O功能(也就是非格式化的I/O)指定字节应从设备传输到内存还是从内存传输到设备。这种传输通过针对单个字节,并提供速度快、容量大的传输。但是对于程序员来说不方便。
程序员通常更喜欢高层次的I/O(也就是格式化的I/O)。因为在这种输出方式中字节被组成了有意义的单元,例如整数、浮点数、字符、字符串或者用户自定义类型。除了大容量文件处理之外,这种面向类型的方法能够满足绝大多数的I/O处理。
1.传统流和标准流
C++的传统流库(<stdio.h>中的函数)允许输入/输出char类型的字符。一个char只占据一个字节的大小,它只能表示一个字符的有限集(例如,ASCII字符集)。然而,许多语言使用的字母表包含更多的符号,这是单字节的char无法表示的。
C++包含标准流库(,之类的库),可以描述这些新的字符。
2.iostream库的头文件
绝大多数C++程序都包含了<iostream>
头文件,该头文件中声明了所有I/O操作所需的基础服务。其中定义了cin、cout、cerr和clog
对象,分别对应于标准输入流、标准输出流、无缓冲的标准错误流和有缓冲的标准错误流。同时还提供了非格式化和格式化的I/O服务。
<iomanip>
头文件中声明了参数化流操纵符(例如,之前我们使用过的setw和setprecision)用于向格式化I/O提供有用的服务。
<fstream>
头文件中声明了文件处理服务。
3.输入/输出流的类的对象
iostream库提供了许多模板来处理一般的I/O操作。例如,类模板basic_istream
支持输入流操作,类模板basic_ostream
支持输出流操作,类模板basic_iostream
同时支持输入流和输出流操作。
I/O流模板层次和运算符重载
模板basic_istream和basic_ostream派生自同一个基模板basic_ios
。模板basic_iostream则从模板basic_istream和basic_ostream多重继承而来。下面是它们的UML类图。
运算符重载为输入和输出操作提供了更加方便的符号。左移运算符<<
被重载用于实现流的输出,被称为流插入运算符
,右移运算符>>
被重载用于实现流的输入,被称为流提取运算符
。这些运算符通常与标准流对象cin、cout、cerr和clog
,以及用户自定义的流对象一起使用。
标准流对象cin、cout、cerr和clog
预定义对象cin
是一个istream类的实例,并且被绑定到标准输入设备(通常是键盘)。在下面的语句中,流提取运算符>>
用于使一个整形变量grade的值从cin输入到内存。
cin >> grade;
注意,由编译器确定grade的数据类型,并且选择合适的流提取运算符重载。假设grade已经被正确的声明,流提取运算符不需要附加任何类型信息(附加类型信息,就像C语言的I/O方式一样,在函数中显式的指定输入数据的类型)。标准库中重载的>>运算符可以用来输入内置类型、字符串和指针的值。
预定义对象cout
是一个ostream类的实例,并且被绑定到标准输出设备(通常是显示器)。在下面的语句中,流插入运算符<<
用于将变量grade的值从内存中输出到标准输出设备:
cout << grade;
注意,这里也是由编译器确定grade的数据类型,并且选择合适的流插入运算符,因此流插入运算符也同样不需要附加类型信息。
预定义对象cerr
和clog
都是ostream类的实例,且都被绑定到标准错误设备。对象cerr的输出是无缓冲的。每个针对cerr的流插入的输出必须立刻显示,这对于迅速提示用户发生错误非常合适。而对象clog是输出是有缓冲的,每个针对clog的流插入的输出先保存到缓冲区中,直到缓冲区被填满或是被清空才会输出。
文件处理模板
C++文件处理乃至类模板basic_ifstream
、basic_ofstream
和basic_fstream
。模板basic_ifstream继承自basic_istream,basic_ofstream继承自basic_ostream,而basic_fstream则继承自basic_iostream。它们的UML类图如下:
二、输出流
ostream提供了格式化的和非格式化的输出功能。输出功能包括使用流插入运算符<<
执行标准数据类型的输出;通过成员函数put
进行字符输出;通过成员函数write
进行非格式化的输出;十进制、八进制、十六进制格式的整数输出;具有不同精确度的浮点数的输出,或是具有强制小数点的浮点数的输出;用指定符号填充数据域的输出;以及使用科学计数法和十六进制符号表示的大写字母的输出。
1.char* 变量的输出
C++能自动判定数据类型,这是它相对于C的一种改进。但是,这一特性有时候会产生一些问题。比如,现在我们要打印一个字符串的char*的值(该字符串首字符的地址)。然而,<<
运算符已被重载用于打印将char*数据类型作为以空字符结尾的字符串。解决的办法就是将char*强制转化为void*类型
(如果程序员想输出一个地址,那么就都应该对指针变量进行这样的转换)。
下面就是分别以字符串形式和地址形式输出char*的值的例子。
#include <iostream>
using namespace std;int main()
{const char* const word = "hehe";cout << "Value of word is " << word<< "\nValue of static_cast<const void*>(word) is " << static_cast<const void*>(word) << endl;
}
运行结果:
2.使用成员函数put进行字符输出
可以使用成员函数put输出字符,例如,语句:
cout.put('A');
就可以显示单个字符A
。put也可以级联使用(该成员函数返回的也是一个ostream对象的引用)。例如,
cout.put('A').put('B');
三、输入流
格式化的和非格式化的输入功能是由istream来提供的。流提取运算符通常跳过输入流中的空白字符(例如,空格、制表符和换行符)
,我们也可以改变它的这种行为。
在每个输入操作之后,流提取运算符给接收到所提取的信息的流对象返回一个引用。如果引用被用作判断条件(例如,while(cin >> grade)
),那么将隐式调用流重载的void*强制转换运算符函数,根据最后输入操作的成功与否,将引用转化为非空指针或是空指针值。 非空指针值转化为bool值true,表示操作成功;空指针值则转化成bool值false,表示操作失败。
当试图超过流的末尾进行读取操作时,流重载的void*强制转化运算符返回一个空指针,表示已经读到文件的末尾。
每个流对象都包含一组状态位来控制流的状态(例如,格式化、设置错误状态等),流重载的void*强制类型转换运算符使用这些状态位来决定是返回非空值还是空值。当输入错误的数据类型时,流提取的failbit
状态位被设置;当操作失败时,流的badbit
位被设置。
1.get和getline成员函数
没有实参的成员函数get从指定流中输入一个字符(包括空白字符及其他非图形字符,比如表示文件尾的键序列等),并将这个值作为函数调用的返回值返回。这个版本的get函数在遇到流中的文件尾时返回EOF
值。
使用成员函数eof、get和put
下面代码展示了成员函数eof、get和put的使用方法。
#include <iostream>
using namespace std;int main()
{int character;cout << "Before input, cin.eof() is " << cin.eof() << endl<< "Enter a sentence followed by end-of-file:" << endl;while ((character = cin.get()) != EOF){cout.put(character);}cout << "\nEOF in this system is: " << character << endl;cout << "After input of EOF, cin.eof() is " << cin.eof() << endl;
}
注意Windows系统中的文件结束符是通过组合键ctrl + z
,UNIX系统中组合键ctrl + d
。
运行结果:
注意,只有当程序试图越过流中的最后一个字符进行读操作时,eof函数才返回true。也就是只有当文件指针指向结束符时,还要再将进行读取操作,此时eof函数才会返回true。
比较cin和cin.get
下面程序对使用流提取cin进行输入(读取字符直到遇到空白字符)和cin.get进行输入的不同之处。注意下面代码中使用get并没有指定分隔符,所以它使用默认的分隔字符(\n
)。
#include <iostream>
using namespace std;int main()
{const int SIZE = 80;char buffer1[SIZE];char buffer2[SIZE];cout << "Enter a sentence: " << endl;cin >> buffer1;cout << "\nThe string read with cin was:" << endl<< buffer1 << endl << endl;cin.get(buffer2, SIZE);cout << "The string read with cin.get was:" << endl<< buffer2 << endl << endl;
}
运行结果:
可以看到cin.get会遇到\n
之前的,小于读取字符数的所有字符。而使用cin进行读取则只会读取到第一次遇到空白字符之前的字符。
成员函数getline
#include <iostream>
#include <string>
using namespace std;int main()
{string str1;string str2;cout << "Enter a sentence: " << endl;getline(cin, str1, ' ');cout << "\nThe string read with getline(cin, str1, ' ') was:" << endl<< str1 << endl << endl;getline(cin, str2);cout << "The string read with get(cin, str2) was:" << endl<< str2 << endl << endl;
}
运行结果:
可以看到使用getline获取的第二个string对象中,首字符并不是空格(被丢弃了)。
2.istream的成员函数peek、putback和ignore
putback这个成员函数对于扫描输入流,在其中搜索以特定字符开头的字段的应用程序很有用。
例如,此时我们要输出一个以字母’a’开头的单词,所以我们需要从流读取一个单词的首字符判断是否符合要求,在读取到一个单词的首字符为’a’时,我们要将这个单词保存到一个变量中,但是因为我们已经从流中读取了它的第一个字符,我们现在需要将这个字符’a’放回到流的最前面,使得我们可以从流中再次读取到这个完整的单词。
该函数也可以实现像上面的putback函数一样的预读
操作。只是它压根没有把字符提取出来,所以也就不需要放回。
3.类型安全
C++提供类型安全的I/O。重载<<
和>>
运算符可接收各种指定类型的数据项,如果遇到意料之外的数据类型,各种相应的错误位就会被设置,用户可以通过检测错误位来判断I/O操作是否成功。如果没有为用户自定义类型重载运算符<<和>>,并且试图输入或输出一个该用户自定义类型的对象的内容,那么编译器就会报错。
四、使用read、write和gcount的非格式化的I/O
下面给出一个使用read、write和gcount的例子。
#include <iostream>
using namespace std;#define SIZE 80int main()
{char buffer[SIZE];cout << "Enter a sentence:" << endl;cin.read(buffer, 20); // 从输入流提取20个字符存入buffer中cout << "The sentence entered was: " << endl;cout.write(buffer, cin.gcount()); // 之前提取了多少个字符,就输出多个字符cout << endl;
}
运行结果:
五、流操纵符简介
C++提供多种流操纵符来完成格式化的任务。流操纵符的功能包括设置域的宽度、设置精确度、设置和取消格式状态、设置域的填充字符、刷新流、向输出流添加新行(并刷新流)、在输出流中添加一个新字符、跳过输入流中的空白,等等。
1.整数流的基数:dec、oct、hex和setbase
为了能够更改流中整数的基数(默认的基数是10,也就是十进制),使之不局限于默认的基数,可以插入hex操纵符基数设置为十六进制,或者插入oct操纵符将基数设置为八进制。插入dec操纵符将整型流的基数重新设置为十进制。
当然除了使用上面提到的流操纵符,还可以使用setbase这个流操纵符来设置基数,该操纵符通过一个整数参数10、8、或16将基数分别设为十进制、八进制或十六进制。由于setbase有一个参数,所以也称为参数化流操纵符
。而要使用任何参数化的操纵符就必须包含<iomanip>
这个头文件。流的基数值只有被显式更改才会发生变化,所以上面提到这些流操纵符的设置都是“黏性”的。
下面给出这几个流操纵符的使用示例:
#include <iostream>
#include <iomanip>
using namespace std;int main()
{int number;cout << "Enter a decimal number: ";cin >> number;cout << number << " in hexadecimal is " << hex << number << endl;cout << dec << number << " in octal is " << oct << number << endl;cout << setbase(10) << number << " in setbase(7) is " << setbase(7) << number << endl<< "in setbase(10) is " << setbase(10) << number << endl;
}
运行结果:
从结果中可以看出setbase流操纵符只能设置为10、8或16这三种基数。
2.浮点精度
可以使用流操纵符setprecision
或ios_base
的成员函数precision
来控制浮点数的精度(也就是小数点右边的位数)。这两种操作的设置都是黏性的。调用无参数的成员函数precision
将返回当前的精度设置。
下面给出一个使用示例:
#include <iostream>
#include <iomanip>
using namespace std;int main()
{double d = 3.14159265359;cout << "The default precision is " << cout.precision() << "\n\n";cout << "With default precision d is " << d << "\n";cout.precision(8);cout << "With high precision d is " << d << "\n\n";cout << "Current precision is " << cout.precision() << "\n\n";cout << setprecision(6) << "With setprecision(6) d is " << d << "\n\n";cout << fixed;cout << "\nAfter setting fixed:\n\n";cout << "With cout.precision(6) d is ";cout.precision(6);cout << d << "\n\n";cout << "With setprecision(8) d is " << setprecision(8)<< d << "\n\n";
}
运行结果:
从结果中看,如果没有使用流操纵符fixed
,成员函数percision
和参数化流操纵符setprecision
设置的整个浮点数的有效数位;而使用fixed
之后,它指定的就是小数点后面的有效位数。
3.位宽(width和setw)
成员函数width
(基类是ios_basic
)可以设置域宽(也就是输出值所占的字符位数或是可输入的最大字符数)并且返回原先的位宽。
如果输出值的宽度比域宽小,则插入填充字符进行填充。宽度大于指定宽度的值不会被截短
,会将整个值都打印出来。
提示
宽度设置只适用于下一次输入或输出(非黏性),之后的宽度被隐式地设置为0(默认设置)。
使用示例:
#include <iostream>
using namespace std;int main()
{int widthValue = 3;char sentence[10];cout << "Enter a sentence:" << endl;cin.width(5); // 最多输入5个字符while (cin >> sentence){cout.width(widthValue++);cout << sentence << endl;cin.width(5);}
}
运行结果:
注意,使用cin读取字符时,会在遇到空白字符时停下,并跳过空白字符,下一次读取直接从下一个非空白字符开始读取。
使用width
成员函数,会读取(参数 - 1)个字符,因为需要在每次读取的字符串后面添加一个\0
。
所以才会出现上面的输出。
4.用户自定义输入流操纵符
程序员可以创建自己的流操纵符。下面的代码中展示了创建和使用新的无参数操纵符bell
、carriageReturn
、tab
和endLine
。对于输出流操纵符来说,它们的返回类型都必须是ostream&
类型的。
#include <iostream>
using namespace std;// 输出一个\a
ostream& bell(ostream& output)
{return output << '\a';
}// 输出一个 \r
ostream& carriageReturn(ostream& output)
{return output << '\r';
}// 输出一个 \t
ostream& tab(ostream& output)
{return output << '\t';
}// 输出一个 endl
ostream& endLine(ostream& output)
{return output << '\n' << flush;
}int main()
{cout << "Testing the tab manipulator:" << endLine<< 'a' << tab << 'b' << tab << 'c' << endLine;cout << "Testing the carriageReturn and bell manipulator:" << endLine<< ".....................";cout << bell;cout << carriageReturn << "---------" << endLine;
}
运行结果:
六、流的格式状态和流操纵符
在I/O流操作中,可以使用多种流操纵符来指定各种格式。流操纵符可以控制输出格式。
下表中列出了类ios_base中可能会使用到的流操纵符。
流操纵符 | 描述 |
---|---|
skipws | 跳过输出流的空白字符。使用流操纵符noskipws来重置设置 |
left | 域的输出左对齐。必要时在右边填充字符 |
right | 域的输出右对齐。必要时在左边填充字符 |
internal | 表示域中的数字的符号左对齐,同时域的数字的数值部分右对齐(中间使用填充字符填充) |
boolalpha | 指定bool类型的值以true或false的形式显示。noboolalpha指定bool类型的值以1或0的形式显示 |
dec | 整数以十进制数显示 |
oct | 整数以八进制数显示 |
hex | 整数以十六进制数显示 |
showbase | 指明在数字的前面显示该数的基数(以0开头表示八进制,0x表示十六进制)。使用流操纵符noshowbase可以取消数字前的基数显示 |
showpoint | 指明浮点数必须显示小数点。通常使用fixed流操纵符来确保小数点右边数字的位数,即使全部为0。可以使用流操纵符noshowpoint重置该设置 |
uppercase | 指明当显示十六进制数时使用大写字母,并且在科学计数法表示浮点数时使用大写字母E。可以使用流操纵符nouppercase来重置该设置 |
showpos | 在正数(负数本来就会显示符号)前显示加号(+)。可以使用流操纵符noshowpos来重置该设置 |
scientific | 以科学计数法显示输出浮点数 |
fixed | 以定点小数形式显示浮点数,并指定小数点右边的位数 |
1.尾数零和小数点(showpoint)
流操纵符showpoint
强制要求浮点数的输出必须带小数点和尾数零。比如说浮点数79.0,在不使用showpoint时显示79,使用showpoint时则显示79.0000(尾数零取决于当前的精确度)。要重置showpoint的设定,需要使用流操纵符noshowpoint
。
当不使用fixed
或scientific
时,精确度表示显示的有效位数,而不是小数点后的数字的位数。
#include <iostream>
using namespace std;int main()
{cout << "Before using showpoint:" << '\n';cout << "6.6600 prints as: " << 6.6600 << endl<< "6.6000 prints as: " << 6.6000 << endl<< "6.0000 prints as: " << 6.0000 << "\n\n";cout << "After using showpoint:" << '\n';cout << showpoint << "6.6600 prints as: " << 6.6600 << endl<< "6.6000 prints as: " << 6.6000 << endl<< "6.0000 prints as: " << 6.0000 << "\n\n";
}
运行结果:
2.对齐(left、right和internal)
直接看下面的例子
#include <iostream>
#include <iomanip>
using namespace std;int main()
{int x = 12345;cout << "Default is right justified:" << endl<< setw(10) << x;cout << "\n\nUse std::left to left justify x:\n"<< left << setw(10) << x;cout << "\n\nUse std::internal to internal justify x:\n"<< showpos << internal << setw(10) << x;cout << "\n\nUse '*' to fill space x:\n"<< setfill('*') << setw(10) << x << endl;}
运行结果:
其中在使用internal流操纵符时,还辅以showpos和setfill这两个流操纵符,其中showpos是显示在数字前显示符号,setfill是设置在输出内容比域宽小时进行填充的字符。
3.内容填充(fill和setfill)
成员函数fill
指定对齐域的填充字符。如果没有字符被指定,则默认使用空格符填充。fill函数返回设定之前的填充字符。setfill流操纵符也用于设置填充字符。
可以参照上一个示例代码。
其中成员函数fill
的使用,只需要在其前面使用一个ostream对象即可,例如:cout.fill('*');
4.整数流的基数
使用流操纵符showbase
可以要求整数的基数被输出。
例如:
#include <iostream>
using namespace std;int main()
{int x = 100;cout << "Printing integers preceded by their base:" << endl<< showbase;cout << x << endl;cout << oct << x << endl;cout << hex << x << endl;
}
运行结果:
5.浮点数、科学计数法和定点小数记数法(scientific、fixed)
“黏性”流操纵符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:\n"<< x << '\t' << y << endl;cout << "\nDisplayed in scientific format:\n"<< scientific << x << '\t' << y << endl;cout << "\nDisplayed in fixed format:\n"<< fixed << x << '\t' << y << endl;
}
运行结果:
6.大写/小写控制(uppercase)
流操纵符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.28e10 << endl<< hex << showbase << 123456789 << endl;
}
运行结果:
7.指定布尔类型(boolalpha)
boolalpha
和noboolalpha
的设置为“黏性”的。直接看下面的示例:
#include <iostream>
using namespace std;int main()
{bool booleanValue = true;cout << "booleanValue is " << booleanValue << endl;cout << "booleanValue (after using boolalpha) is "<< boolalpha << booleanValue << "\n\n";cout << "\nswitch booleanValue and use noboolalpha" << endl;booleanValue = false;cout << noboolalpha << endl;cout << "booleanValue is " << booleanValue << endl;cout << "booleanValue (after using boolalpha) is " << boolalpha << booleanValue << endl;
}
运行结果:
8.通过成员函数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 << "\n\n";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 new format:\n"<< integerValue << '\t' << doubleValue << "\n\n";cout.flags(originalFormat); // 重置格式状态cout << "The restored value of the flags variable is: " << cout.flags()<< "\nPrint values in original format again:\n"<< integerValue << '\t' << doubleValue << "\n\n";
}
运行结果:
七、流的错误状态
流的状态可以通过检测ios_base
类中的相应位来判断。
注意,在绝大多数情况下,如果eofbit被设置了,那么failbit也会被设置。
#include <iostream>
using namespace std;int main()
{cout << "Before a bad input operation:"<< "\ncin.rdstate(): " << cin.rdstate()<< "\ncin.eof(): " << cin.eof()<< "\ncin.fail(): " << cin.fail()<< "\ncin.bad(): " << cin.bad()<< "\ncin.good(): " << cin.good() << "\n\n";int number;cout << "Expects an integer, but enter a character: ";cin >> number;cout << "\nAfter a bad input operation:"<< "\ncin.rdstate(): " << cin.rdstate()<< "\ncin.eof(): " << cin.eof()<< "\ncin.fail(): " << cin.fail()<< "\ncin.bad(): " << cin.bad()<< "\ncin.good(): " << cin.good() << "\n\n";cin.clear(); // 重置错误状态,该函数的默认参数是std::ios_base::goodbitcout << "After cin.clear()" << "\ncin.rdstate(): " << cin.rdstate()<< "\ncin.eof(): " << cin.eof()<< "\ncin.fail(): " << cin.fail()<< "\ncin.bad(): " << cin.bad()<< "\ncin.good(): " << cin.good() << "\n\n";
}
运行结果:
当遇到文件尾时,输入流有eofbit将被设置。当试图越过流的末尾提取数据时,程序可以调用成员函数eof
来判断是否遇到了文件尾。遇到文件尾返回true
,否则返回false
。
当在流中发生格式错误时,failbit位将被设置,并且不会读入任何字符,例如,像上面的测试程序一样,预期从流中接收一个整数,但是接收到一个字符,在遇到这种情况时,这些字符不会丢失。成员函数fail
将报告流操作是否失败了。通常这种错误是可恢复的(数据没有丢失)。
当发生数据丢失错误时,将会设置badbit
位。成员函数bad
将报告流操作是否失败了。一般情况下,这种严重的错误是不可恢复的(数据已经丢失)。
如果流中的eofbit、failbit、badbit
都没有被设置,那么goodbit
将设置。
成员函数rdstate
返回流的错误状态。可以使用该函数的返回值来判断错误状态,例如,将该函数的返回值与不同的错误位进行"相与",结果为1的即是出现错误的状态位。
成员函数clear
重置流的错误状态,默认参数为goodbit
,既将流的状态设为good
。
所以也可以使用下面的语句将流的状态设为错误cout.clear(ios::failbit);