8. IO 库(重点)
c++ 语言不直接处理输入输出,而是通过一簇定义在标准库中的类型来处理 IO 。
8.1 IO 类
我们已经使用过的 IO 类型和对象都是操纵 char 数据的。 iostream 定义了用于读写流的基本类型, fstream 定义了读写命名文件的类型, sstream 定义了读写内存 string 对象的类型。为了支持使用宽字符的语言,标准库定义了一组类型和对象来操纵 wchar_t 类型的数据,宽字符版本的类型和函数的名字以一个 w 开始。
标准库使我们能忽略这些不同类型的流之间的差异,这是通过继承机制实现的。简单地说,继承机制使我们可以声明一个特定的类继承自另一个类,通常可以将一个派生类(继承类)对象当作其基类(所继承的类)对象来使用。
由于不能拷贝 IO 对象,进行 IO 操作的函数通常以引用方式传递和返回流。IO 操纵一个与生俱来的问题是可能发生错误。
一个流一旦发生错误,其上后续的 IO 操作都会失败。由于流可能处于错误状态,因此代码通常应该在使用一个流之前检查它是否处于良好状态。
IO 库定义了一个与机器无关的 iostate 类型,它提供了表达流状态的完整功能。badbit 表示系统级错误,如不可恢复的读写错误。在发生可恢复错误后,failbit 被置位,如期望读取数值却读出一个字符等错误。如果 badit、failbit 和 eofbit 任一个被置位,则检测流状态的条件会失败。实际上,我们将流当作条件使用的代码就等价于 !fail( )。而 eof 和 bad 操作只能表示特定的错误。
流对象的 rdstate 成员返回一个 iostate 值,对应流的当前状态。setstate 操作将给定条件位置位,表示发生了对应错误。clear 不接受参数的版本清除(复位)所以错误标志位。执行 clear( ) 后,调用 good 会返回 true。带参数的 clear 版本接受一个 iostate 值,表示流的新状态。
每个输出流都管理一个缓冲区,用来保存程序读写的数据。有了缓冲机制,操作系统就可以将程序的多个输出操作组合成单一的系统级写操作。由于设备的写操作可能很耗时,允许操作系统将多个输出操作组合为单一的设备写操作可以带来很大的性能提升。
endl 完成换行并刷新缓冲区的工作;flush 刷新缓冲区,但不输出任何额外的字符;ends 向缓冲区插入一个空字符,然后刷新缓冲区。
当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流。每个流同时最多关联到一个流,但多个流可以同时关联到同一个 ostream 。
cin.tie( &cout ); // 仅仅用来展示:标准库将 cin 和 cout 关联在一起
ostream *old_tie = cin.tie( nullptr ); // cin 不再与其他流关联
8.2 文件输入输出
头文件 fstream 定义了三个类型来支持文件 IO : ifstream 从一个给定文件读取数据,ofstream 向一个给定文件写入数据,以及 fstream 可以读写给定文件。
在 c++11 标准中,文件名既可以是库类型 string 对象,也可以是 C 风格字符数组,而旧版本的标准库只允许 C 风格字符数组。
因为调用 open 可能失败,进行 open 是否成功的检测通常是一个好习惯:
if (fstreamname) // 检查 open 是否成功,如若成功就可以使用文件了
当一个 fstream 对象被销毁时,close 会自动被调用。
每个流都有一个关联的文件模式(file mode),用来指出如何使用文件。保留被 ofstream 打开的文件中已有数据的唯一方法是显式指定 app 或 in 模式。在每次打开文件时,都要设置文件模式,可能是显示地设置,也可能是隐式地设置。当程序未指定时,就使用默认值。
8.3 string 流
istringstream 从 string 读取数据,ostringstream 向 string 写入数据,而头文件 stringstream 既可从 string 读数据也可向 string 写数据。
当我们的某些工作是对整行文本进行处理,而其他一些工作是处理行内的单个单词时,通常可以使用 istringstream 。
当我们逐步构造输出,希望最后一起打印时,ostringstream 是很有用的。
小结
C++ 使用标准库类来处理面向流的输入与输出:
iostream 处理控制台 IO
fstream 处理命名文件 IO
stringstream 完成内存 string 的 IO
每个 IO 对象都维护一组条件状态,用来指出此对象上是否可以进行 IO 操作。
待完善…