C++的输入/输出由标准库提供。标准库定义了一组类型,支持对文件和控制窗口等设备的读写(IO)。还定义了其他一些类型,使string对象能够像文件一样操作,从而使我们无需IO就能实现数据与字符之间的转换。一般来说,类的设计者还可以很方便地使用IO标准库设施读写自定义类的对象。类类型通常使用IO标准库为内置类型定义的操作符和规则来进行读写。
一、面向对象的标准库
迄今为止,我们已经使用IO类型和对象读写数据流,它们常用于与用户控制窗口的交互。当然,实际的程序不能仅限于对控制窗口的IO,通常还需要读或写已命名的文件。此外,程序还应该能方便地使用IO操作格式化内存中的数据,从而避免读写磁盘或其他设备的复杂性和运行代价。应用程序还需要支持宽字符语言的读写。
一般而言,通过继承关联起来的类型都共享共同的接口。当一个类继承另一个类时,这两个类通常可以使用相同的操作。更确切地说,如果两种类型存在继承关系,则可以说一个类“继承”了其父类的行为——接口。
IO类型在三个独立的头文件中定义:iostream定义读写控制窗口的类型,fstream定义读写已命名文件的类型,而sstream所定义的类型则用于读写存储在内存中string对象。在fstream和sstream里定义的每种类型都是从iostream头文件中定义的相关类型派生而来。
对IO类型使用继承还有另外一个重要的含义:如果函数有基类类型的引用形参时,可以给函数传递其派生类型的对象。这就意味着:对istream&进行操作的函数,也可使用ifstream或者istringstream对象来调用。
1.1 国际字符的支持
每一个IO头文件都定义了char和wchar_t类型的类和标准输入/输出对象。基于流的wchar_t类型的类和对象在iostream中定义,宽字符文件流类型在fstream中定义,而宽字符stringstream则在sstream头文件中定义。
1.2 IO对象不可复制或赋值
一般情况下,如果要传递IO对象以便对它进行读写,可用非const引用的方式传递这个流对象。对IO对象的读写会改变它的状态,因此引用必须是非const的。
二、条件状态
实现IO的继承正式错误发生根源。一些错误是可恢复的;一些错误则是发生在系统底层,位于程序可修正的范围之外。IO标准库管理一些列条件状态成员,用来标记给定的IO对象是否处于可用状态,或者碰到了哪种特定的错误。
函数 | 标记 |
strm::iostate | 机器相关的整型名,由各个iostream类定义,用于定义条件状态 |
strm::badbit | strm::iostate类型的值,用于指出被破坏的流 |
strm::failbit | strm::iostate类型的值,用于指出失败的IO操作 |
strm::eofbit | strm::iostate类型的值,用于指出流已经达到文件结束符 |
s.eof() | 如果设置了流s的eofbit值,则该函数返回true |
s.fail() | 如果设置了流s的failbit值,则该函数返回true |
s.bad() | 如果设置了流s的badbit值,则该函数返回true |
s.good() | 如果流s处于有效状态,则该函数返回true |
s.clear() | 将流s中的所有状态值都重设为有效状态 |
s.clear(flag) | 将流s中的某个指定条件状态设置为有效。flag的类型是strm::iostate |
s.setstate(flag) | 给流s添加指定条件。flag的类型是strm::iostate |
s.rdstate() | 返回流s的当前条件,返回值类型为strm::iostate |
2.1条件状态
所有流对象都包含一个条件状态成员,该成员由setstate和clear操作管理。
2.2 流状态的查询和控制
三、输出缓冲区的管理
3.1 输出缓冲区的刷新
endl:用于输出一个换行符并刷新缓冲区
flush:用于刷新流,但不在输出中调价任何字符
ends:比较少用,在缓冲区中插入空字符null,然后再刷新它
3.2 unitbuf操纵符
如果需要刷新都有输出,最好使用unitbuf操纵符。这个操纵符在每次执行完些操作后都刷新流。
3.3 将输入和输出绑定在一起
交互式系统通常应确保它们的输入和输出流是绑定在一起的。这样做意味着可以保证任何输出,包括给用户的提示,都在试图读之前输出。
四、文件输入和输出
fstream头文件定义了三种支持文件IO的类型:
(1)ifstream,由istream派生而来,提供读文件的功能;
(2)ofstream,由osteam派生而来,提供写文件的功能;
(3)fstream,由iostream派生而来,提供读写同一个文件的功能。
4.1文件流对象的使用
需要读写文件时,则必须定义自己的对象,并将他们绑定在需要的文件上。
在使用fstream对象之前,还必须使这些对象捆绑要读写的文件。
调用open成员函数将已存在的fstream对象与特定文件绑定。为了实现读写,需要将指定的文件打开并定位,open函数完成系统指定的所有需要的操作。
4.1.1 检查文件打开是否成功
打开文件后,通常要检验打开是否成功,这是一个好习惯。
ifstream infile;
infile.open("in");
if(!infile)
{
...........................
return -1;
}
4.1.2 将文件流与新文件重新捆绑
fstream对象一旦打开,就保持与指定的文件相关联。如果要把fstream对象与另一个不同的文件关联,则必须先关闭(close)现有的文件,然后打开(open)另一个文件。
4.1.3 清除文件流的状态
如果程序员需要用文件流读写多个文件,必须在读另一个文件之前调用clear清除该流的状态。
4.2 文件模式
在打开文件时,无论是调用open还是以文件名作为流初始化的一部分,都需要指定文件模式。每个fstream类都定义了一组表示不同模式的值,用于指定流打开的不同模式。与条件状态标识一样,文件模式也是整型常量。文件流构造函数和open函数都提供了默认实参设置文件模式。
in | 打开文件做读操作 |
out | 打开文件做写操作 |
app | 在每次写之前找到文件尾 |
ate | 打开文件后立即将文件定位在文件尾 |
trunc | 打开文件时清空已存在的文件流 |
binary | 以二进制模式进行IO操作 |
out、trunc和app模式只能用于指定与ofstream或fstream对象关联的文件;in模式只能用于指定与ifstream或fstream对象所关联的文件。所有的文件都可以使用ate或binary模式打开。ate模式只在打开时有效:文件打开后将定位到文件尾。以binary模式打开的流则将文件以字节序列的形式处理,而不解释流中的字符。
默认时,与ifstream流对象关联的文件以in模式打开,该模式允许文件做读的操作;与iostream关联的文件则以out模式打开,是文件可写。以out模式打开的文件会被清空:丢弃该文件存储的所有数据。
注:从效果来看,为ofstream对象执行out模式等效于同时指定了out和trunc模式。
4.2.1 对同一个文件作输出和输入运算
默认情况下,fstream对象以in和out模式同时打开。当文件同时以in和out打开时不清空。如果打开fstream所关联的文件时,只使用out模式,而不指定in模式,则文件会清空已存在的数据。如果打开文件时指定了trunc模式,则无论是否同时指定了in模式,文件同样会被清空。
4.2.2 模式是文件的属性而不是流的属性
注:只要调用open函数,就要设置文件模式,其模式的设置可以是显式的也可以是隐式地。如果没有指定文件模式,将使用默认值。
4.2.3 打开模式的有效组合
并不是所有的打开模式都是可以同时指定。有些模式组合式没有意义的:
out | 打开文件做写操作,删除文件中已有的数据 |
out | app | 打开文件做写操作,在文件尾写入 |
out | trunc | 与out模式相同 |
in | 打开文件做读操作 |
in | out | 打开文件做读、写操作,并定位于文件开头处 |
in | out | trunc | 打开文件做读、写操作,删除文件中已有的数据 |
4.3 一个打开并检查输入文件的程序
五、字符串流