第八章 IO库
IO类
为了支持不同种类的IO处理操作,标准库定义了这几种类型:
-
iostream 定义了用于读写流的基本类型
-
fstream 定义了读写命名文件的类型
-
sstream 定义了读写内存string对象的类型
它们分别定义在同名的头文件中。
IO类型间的关系
类型ifstream和istringstream都继承自istream。我们可以像使用istream对象一样来使用它们。对于ostream也是如此。
IO对象无拷贝或赋值
由于不能拷贝IO对象,因此也不能将形参或返回类型设置为流类型。进行IO操作的函数通常以引用方式传递或返回流。
读写一个IO对象会改变其状态,因此传递和返回的引用不能是const的。
条件状态
IO类定义了一些函数和标志,可以帮助我们访问和操纵流的条件状态。见p279。
一个IO错误的例子:
int ival;
cin >> ival;
如果试图在标准输入上键入Boo,读操作就会失败,cin进入错误状态。
如果输入一个文件结束符标识,cin也会进入错误状态。
一个流一旦发生错误,其上后续的IO操作都会失败。确定一个流对象的状态的最简单的方法是将它当作一个条件来使用:
while (cin >> word)
// ok
Note
读取失败后,不会消耗掉缓冲区的内容。因此这种情况,通常读取一个字符串,然后转换字符串为数字。
管理输出缓冲
每个输出流都管理一个缓冲区,用来保存程序读写的数据。如果执行下面的代码:
os << "please enter a value: ";
文本串可能立即打印出来,但也有可能被操作系统保存在缓冲区中,随后再打印。这样可以带来很大的性能提升。
导致缓冲区刷新的原因有:
-
程序正常结束
-
缓冲区满时
-
使用操纵符,如endl,来显式刷新缓冲区
-
读cin或写cerr,都会导致cout的缓冲区被刷新
刷新输出缓冲区
IO库还提供了两个操纵符用于刷新缓冲区:
-
flush 刷新缓冲区,但不输出任何额外字符
-
ends 向缓冲区插入一个空字符,然后刷新缓冲区
unitbuf操纵符
如果想在每次输出操作后都刷新缓冲区,我们可以使用unitbuf操纵符。
cout << unitbuf; // 所有输出操作后都会立即刷新缓冲区
cout << nounitbuf; // 回到正常的缓冲方式
Warning
如果程序崩溃,输出缓冲区不会刷新
文件输入输出
除了继承自iostream类型的行为之外,fstream中定义的类型还增加了一些新的成员来管理与流关联的文件。见p283。
使用文件流对象
当想要读写一个文件时,可以定义一个文件流对象,并将对象与文件关联起来。
每个文件流类都定义了一个名为open的成员函数,它完成一些系统相关的操作,来定位给定的文件,并视情况打开为读或写模式。
创建文件流对象时,如果提供了一个文件名,则open会被自动调用:
ifstream in(file); // 构造一个ifstream并打开给定的文件
ofstream out; // 输出文件流未关联到任何文件
Note
当一个fstream对象被销毁时,close会自动被调用。
文件模式
每个流都有一个关联的文件模式,用来指出如何使用文件。见p286。
每个文件流类型都定义了一个默认的文件模式,当未指定文件模式时,就使用此默认模式。
-
与ifstream关联的文件默认以in模式打开;
-
与ofstream关联的文件默认以out模式打开;
-
与fstream关联的文件默认以in和out模式打开。
以out模式打开文件会丢失已有数据
默认情况下,当我们打开一个ofstream时,文件的内容会被丢弃。
阻止丢弃的方法是同时指定app模式:
ofstream out("file1"); // 文件被截断
ofstream app("file2", ofstream::app); // 保留文件内容,写操作在文件末尾进行
string流
sstream头文件定义了三个类型来支持内存IO:
-
istringstream从string读取数据。
-
ostringstream向string写入数据。
-
stringstream既可以从string读数据,也可以向string写数据。
sstream增加了一些成员来管理与流相关联的string。见p287。
使用istringstream
当我们的某些工作是对整行文本进行处理,而其他一些工作是处理行内的单个单词时,通常可以使用istringstream。
使用ostringstream
当我们逐步构造输出,希望最后一期打印时,ostringstream是很有用的。