IO类
前面提到的IO类型和对象都是操纵char数据的,并且都是关联到用户的控制台窗口的。我们还有其他IO需求:
- 除了从控制台进行
IO操作,应用程序还经常需要读写文件 - 除了操纵
char数据还需要操纵string
为了支持上述操作,在istream和ostream,标准库还定义了一些其他IO类型。分别定义在三个独立的头文件中:
-
iostream:定义了用于读写流的基本类型 -
fstream:定义了读写命名文件的类型 -
sstream:定义了读写内存string对象的类型
标准库通过继承机制
inheritance是我们可以忽略不同类型的流之间的差异。比如ifstream和istringstream都继承自istream,因此我们可以像使用istream对象一样来使用ifstream和istringstream对象。我们是如何使用cin的也可以同样地使用这些类型的对象。比如可以对一个ifstream或istringstream对象调用getline,也可以使用>>从一个ifstream或istringstream对象中读取数据。
1. IO对象无拷贝或者赋值
我们不能拷贝或对IO对象赋值,因此我们也不能将形参或返回类型设置为流类型。进行IO操作时通常是以引用方式传递和返回流。读写一个IO对象会改变其状态,因此传递和返回的引用是非const的。
2. 条件状态
IO操作与生俱来的问题是可能发生错误,一些错误是可修复的,而其他错误则可能发生在系统深处超出了应用程序可以修正的范围。下面列出来IO类所定义的一些函数和标记:
-
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):将流s的对应条件状态复位 -
s.setstate(flags):设置流s的对应条件状态
一个流一旦发生错误,其后续的IO操作都会失败,因此代码通常需要在使用一个流之前检查它是否处于良好状态,确定一个流对象的最简单方法是将它作为一个条件使用:
while (cin >> word)
// ok: 读操作成功...
将流作为条件使用,只能告诉我们流是否有效而无法告诉我们具体发生了什么,我们有时候需要知道错误的具体原因以及是否能恢复。IO库定义了一个与机器无法的iostate类型:
-
badbit:表示系统级错误,一旦badbit被置位,流一般也无法使用了 -
failbit:发生可恢复错误时,failbit被置位,比如期望读取数值却读到一个字符 - 到达文件结束时,
eofbit和failbit都会被置位 -
goodbit:值为0表示流未发生错误,只要badbit、eofbit和failbit中任一个被置位,则表示发生错误
使用
fail()和good()是确定流总体状态的正确方法,而eof和bad操作用于确定具体的错误。
3. 管理输出缓冲
每一个输出流都管理一个缓冲区,比如执行输出代码时文本串可以被立即打印出来,也可能被操作系统保存在缓冲区中用于将多个输出操作组合为单一的系统级写操作。
这主要是因为设备的写操作可能很耗时,操作系统将多个输出操作组合成单一的设备写操作可以极大提升性能。
缓冲刷新,即数据真正写到输出设备或文件的原因有如下:
- 程序正常结束:作为
main函数的return操作的一部分,执行缓冲刷新 - 缓冲区满时:刷新缓冲方便新的数据写入缓冲区
- 使用操纵符
endl来显式刷新缓冲区 - 每个输出操作之后,我们可以使用操纵符
unitbuf设置流的内部状态来清空缓冲区。默认情况下,对cerr是设置的unitbuf的,因此写到cerr的内容都是立即刷新的 - 一个输出流可能被关联到另一个流,在这种情况下读写被关联的流时,关联到的流的缓冲区会被刷新,比如
cin和cerr都关联到cout,读cin或写cerr都会导致cout的缓冲区被刷新
控制缓冲的操纵符:
-
endl:输出换行符并刷新缓冲区 -
flush:不附加任何额外字符,刷新缓冲区 -
ends:输出一个空字符并刷新缓冲区 -
unitbuf:所有输出操作后都立即刷新缓冲区 -
nounitbuf:回到正常的缓冲方式
需要注意的是,如果程序崩溃,输出缓冲区不会被刷新,调试一个已经崩溃的程序时,需要确认输出数据是不是因为被挂在缓冲区而没有打印
交互式系统都应该关联输入流和输出流,这意味着所有输出包括用户提示信息都会在读操作之前被打印出来。
文件输入输出
1. 类型及操作
头文件fstream定义了三个类型来支持文件IO:
-
ifstream:从一个给定文件读取数据 -
ofstream:向一个给定文件写入数据 -
fstream:读写给定文件
上面提到的类型继承了cin和cout操作(比如>>、<<和getline等),fstream还包括其他特有的操作:
-
fstream fstrm(s);:创建一个fstream并打开名为s的文件,其中s可以是string也可以是C风格字符串指针,这些构造函数都是explict的 -
fstream fstrm(s, mode);:和前一个构造函数类似,但按指定模式打开文件 -
fstrm.open(s):打开名为s的文件,并将文件与fstrm绑定 -
fstrm.close():关闭与fstrm绑定的文件,并返回void -
fstrm.is_open():判断与fstrm的文件是否成功打开且尚未关闭
2. 使用文件流对象
ifstream in(ifile); // 构造一个ifstream并打开给定文件
ofstream out; // 构造输出文件流,并未关联到任何文件
在要求使用基类型对象的地方,我们可以用继承类型的对象代替,这意味着接受一个iostream类型引用(或指针)参数的函数可以用一个对应的fstream或sstream类型来调用。
如果我们定义了一个空文件流对象,随后可以用open来将它与文件关联起来:
ifstream in(ifile); // 构造一个ifstream并打开文件
ofstream out; // 输出文件流未与任何文件相关联
out.open(ifile + ".copy"); // 打开指定文件
// 如果调用open失败的话,`failbit`会被置位
if (out) // 检查open是否成功,成功的话我们就可以写入文件
一旦一个文件流已经打开,他就会保持与对应文件的关联,如果对一个恶已经打开的文件流调用
open会失败,并会导致failbit被置位,因此文件流关联到另外一个文件时需要先关闭已关联的文件。
3. 自动构造和析构
- 当一个
fstream对象离开其作用域时,与之关联的文件会自动关闭 - 当一个
fstream对象被销毁时,close会自动被调用
4. 文件模式
-
in:读方式打开 -
out:写方式打开 -
app:每次写操作前均定位到文件末尾 -
ate:打开文件后立即定位到文件末尾 -
trunc:截断文件 -
binart:以二进制方式进行IO
与
ifstream关联的文件默认以in模式打开,与ofstream关联的文件默认以out模式打开,与fstream关联的文件默认以in和out模式打开。
如果我们以out模式打开文件时文件的内容会被全部丢弃,阻止一个ofstream清空给定文件内容的方法是同时制定app模式:
// 下面几条语句中,file1都会被截断
ofstream out("file1"); // 默认以out模式打开
ofstream out("file1", ofstream::out); // 隐含地截断文件
ofstream out("file1", ofstream::out | ofstream::trunc);
// 为了保留文件内容,必须显式指定app模式
ofstream app("file2", ofstream::app); // 隐含为输出模式
ofstream app("file2", ofstream::out | ofstream::app);
保留被
ofstream打开的文件中已有数据的唯一方法是显式制定app或in模式。
string流
-
istringstream:从string读取数据 -
ostringstream:向string写入数据 -
stringstream:既可以从string中读数据,也可以向string写数据
stringstream特有的操作包括:
-
sstream strm(s):strm是一个sstream对象,保存string s的一个拷贝,此构造函数是explict的 -
strm.str():返回strm所保存的string的拷贝 -
strm.str(s):将string s拷贝到strm中,返回void
博客介绍了编程语言中IO类、文件输入输出和string流相关知识。IO类对象无拷贝或赋值,有条件状态且需管理输出缓冲;文件输入输出涉及类型操作、文件流对象使用、自动构造析构及文件模式;string流有读写及读写兼备类型,还有特有操作。
33万+

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



