目录
第八章 IO库
前言
8.1 IO类
8.2 文件输入输出
8.3 string流
C++语言不直接处理输入输出,而是通过一族定义在标准库中的类型来处理IO。这些类型支持从设备读取数据、向设备写入数据的IO操作,设备可以是文件、控制台窗口等。还有一些类型允许内存IO,即从string读取数据,向string写入数据。
IO库定义了读写内置类型值的操作,此外,一些类如string通常也会定义类似的IO操作,来读写自己的对象。之前已经使用过的IO库设施小结:
istream | 输入流类型,提供输入操作 |
ostream | 输出流类型,提供输出操作 |
cin | 一个istream对象,从标准输入读取数据 |
cout | 一个ostream对象,向标准输出写入数据 |
cerr | 一个ostream对象,通常用于输出程序错误信息,写入到标准错误 |
>> | 用来从一个istream对象读取输入数据 |
<< | 用来向一个ostream对象写入输出数据 |
getline函数 | 从一个给定的istream对象读取一行数据,存入一个给定的string对象中 |
目前为止,我们使用的IO类型和对象都是操纵char数据的,并且默认情况下,这些对象都是关联到用户的控制台窗口的。而应用程序常常需读写命名文件,且处理string中的字符会很方便。因此,标准库还定义了其他一些IO类型。
头文件 | 类型 |
iostream | istream,wistream从流读取数据 |
ostream,wostream向流写入数据 | |
iostream,wiostream读写流 | |
fstream | ifstream,wifstream从文件读取数据 |
ofstream,wofstream向文件写入数据 | |
fstream,wfstream读写文件 | |
sstream | istringstream,wistringstream从string读取数据 |
ostringstream,wostringstream向string写入数据 | |
stringstream,wstringstream读写string |
注意:宽字符版本的类型和函数的名字以一个w开始,例如,wcin、wcout和wcerr。
Ⅰ)IO类型间的关系:由于类的继承机制,设备类型和字符大小不影响所执行的IO操作。例如,用 >> 读取数据,而不用考虑是从一个控制台窗口,一个磁盘文件还是一个string读取。
Ⅱ)IO对象无拷贝或赋值
ofstream out1, out2; out1 = out2; // 错误!不能对流对象赋值 ofstream print(ofstream); // 错误!不能初始化ofstream参数 out2 = print(out2); // 错误!不能拷贝流对象
注意:不能拷贝IO对象,因此也不能将形参或返回类型设置为流类型。进行IO操作的函数通常以引用方式传递和返回流。读写一个IO对象会改变其状态,因此传递和返回的引用不能是const的。
Ⅲ)流的条件状态
strm::iostate | 提供了表达条件状态的完整功能 |
strm::badbit | 流已崩溃 |
strm::failbit | 指出一个IO操作失败了 |
strm::eofbit | 指出流到达了文件结束 |
strm::goodbit | 指出流未处于错误状态,此值保证为零 |
s.eof( ) | 若流s的eofbit置位,返回true |
s.fail( ) | 若流s的failbit或badbit置位,返回true |
s.bad( ) | 若流s的badbit置位,返回true |
s.good( ) | 若流s处于有效状态,返回true |
s.clear( ) | 将流s中所有条件状态位复位,将流的状态设置为有效,返回void |
s.clear(flags) | 根据给定的flags标志位,将流s中对应条件状态位复位,返回void |
s.setstate(flags) | 根据给定的flags标志位,将流s中对应条件状态位置位,返回void |
s.rdstate( ) | 返回流s的当前条件状态,返回值类型为strm::iostate |
在使用流之前,检查它是否处于良好状态。确定一个流对象的状态的最简单的方法是将它当作一个条件来使用。
int ival; cin >> ival; // 如果在标准输入上键入Boo,读操作就会失败。若输入文件结束标识,cin也会进入错误状态。 while (cin >> word) // ok: 读取成功
查询流的状态:有时我们需要知道流为什么失败,IO库定义了一个与机器无关的iostate类型,提供了表达流状态的完整功能。使用eof、fail、bad和good函数来查询流当前的状态。
管理条件状态:使用clear、setstate和rdstate函数管理条件状态。
auto old_state = cin.rdstate(); // 记住cin的当前状态 cin.clear(); // 使cin有效 process_input(cin); // 使用cin cin.setstate(old_state); // 将cin置为原有状态
Ⅳ)管理输出缓冲:刷新输出缓冲区可以使用endl(再输出一个换行)、flush和ends(再输出一个空字符),如果想在每次输出后都刷新缓冲区,可以使用unitbuf操纵符。当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出符。
cout << unitbuf; // 所有输出都刷新缓冲区 cout << nounitbuf; // 回到正常的缓冲方式 cin >> ival; // 标准库将cin和cout关联在一起,此语句也会导致cout的缓冲区被刷新
我们既可以将一个istream对象关联到另一个ostream,也可以将一个ostream关联到另一个ostream。
cin.tie(&cout); // old_tie 指向当前关联到cin的流(如果有的话)。这句仅仅用来展示:标准库已经默认将cin和cout关联在一起 ostream *old_tie = cin.tie(nullptr); // cin不再与其他流关联 cin.tie(&cerr); // 读取cin会刷新cerr,而不是cout。这不是一个好主意,因为cin应该关联到cout cin.tie(old_tie); // 重建cin和cout间的正常关联
除了继承自iostream类型的行为之外,fstream中定义的类型还增加了一些新的成员来管理与流相关的文件。
fstream fstrm; | 创建一个未绑定的文件流。fstream是头文件fstream中定义的一个类型 |
fstream fstrm(s); | 创建一个fstream,并打开名为s的文件。s可以是string类型,或者是一个指向C风格字符串的指针。 |
fstream fstrm(s, mode); | 和上一个相似,但按指定mode打开文件 |
fstrm.open(s) | 打开名为s的文件,并将文件与fstrm绑定 |
fstrm.close( ) | 关闭与fstrm绑定的文件,返回void |
fstrm.is_open( ) | 返回一个bool值,指出与fstrm关联的文件是否成功打开且尚未关闭 |
Ⅰ)使用文件流对象
当我们想要读写一个文件时,可以定义一个文件流对象,并将对象与文件关联起来。
ifstream in(ifile); // 构造一个ifstream并打开给定文件 ofstream out; // 定义了一个输出流out,输出文件流未关联到任何文件 // 这段代码定义了一个输入流in,它被初始化为从文件读取数据,文件名由string类型的参数ifile指定。
由于继承机制,我们可以用fstream代替iostream&
ifstream input(argv[1]); // 打开销售记录文件 ofstream output(argv[2]); // 打开输出文件 Sales_data total; // 保存销售总额的变量 if (read(input, total)) { // 读取第一条销售记录 Sales_data trans; // 保存下一条销售记录的变量 while (read(input, trans)) { // 读取剩余记录 if (total.isbn() == trans.isbn()) // 检查isbn total.combine(trans); // 更新销售总额 else { print(output, total); // 打印结果 total = trans; // 处理下一本书 } } print(output, total) << endl; // 打印最后一本书的销售额 } else cerr << "No data?!" << endl; // 文件中无输入数据
Ⅱ)成员函数open和close
ifstream in(ifile); // 构筑一个ifstream并打开给定文件 ofstream out; // 输出文件流未与任何文件关联 out.open(ifile + ".copy"); // 打开指定文件 if (out) // 检查open是否成功,与之前用cin用作条件相似 in.close(); // 关闭文件 in.open(ifile + "2"); // 打开另一个文件
Ⅲ)自动构造和析构:当一个fstream对象被销毁时,close会被自动调用。
Ⅳ)文件模式:每个流都有一个关联的文件模式。
in | 以读方式打开 |
out | 以写方式打开(会丢弃已有数据) |
app | 每次写操作前均定位到文件末尾 |
ate | 打开文件后立即定位到文件末尾 |
trunc | 截断文件 |
binary | 以二进制方式进行IO |
sstream strm; | strm是一个未绑定的stringstream对象 |
sstream strm(s); | strm是一个sstream对象,保存string s的一个拷贝 |
strm.str( ) | 返回strm所保存的string的拷贝 |
strm.str(s) | 将string s拷贝到strm中,返回void |
相关参考教程: C++ 文件和流 | 菜鸟教程 (runoob.com)
C++输入输出流 (biancheng.net)
C++文件操作 (biancheng.net)