第8章 IO库
- C++语言不直接处理输入输出,而是通过一族定义在标准库中的类型来处理IO。这些类型支持从设备读取数据、向设备写入数据的IO操作,设备可以是文件、控制台窗口等。
- IO库设施:
- istream(输入流)类型,提供输入操作。
- ostream(输出流)类型,提供输出操作。
- cin,一个istream对象,从标准输入读取数据。
- cout,一个ostream对象,向标准输出写入数据。
- cerr,一个ostream对象,通常用于输出程序错误消息,写入到标准错误。
>>
运算符,用来从一个istream对象读取输入数据。<<
运算符,用来向一个ostream对象写入输出数据。- getline函数,从一个给定的istream读取一行数据,存入一个给定的string对象中。
8.1 IO类
- 标准库还定义了其他一些IO类型,分别在三个独立的头文件中:
- iostream定义了用于读写流的基本类型。
- fstream定义了读写命名文件的类型。
- sstream定义了读写内存string对象的类型。
- 概念上,设备类型和字符大小都不会影响我们要执行的IO操作。
- 标准库使我们能忽略这些不同类型的流之间的差异,这是通过继承机制实现的。
8.1.1 IO对象无拷贝或赋值
- 我们不能拷贝或对IO对象赋值,由于不能拷贝IO对象,因此我们也不能将形参或返回类型设置为流类型。
ofstream out1, out2;
out1 = out2;
ofstream print(ofstram);
out2 = print(out2);
8.1.2 条件状态
- IO操作的问题是可能发生错误。
- IO类所定义的一些函数和标志,可以帮助我们访问和操纵流的条件状态。
函数与标记 | 用法 |
---|
strm::iostate | strm是一种IO类型,iostate是一种机器相关的类型,提供了表达条件状态的完整功能 |
strm::badbit | strm::badbit用来指出流已崩溃 |
strm::failbit | strm::failbit用来指出一个IO操作失败了 |
strm::eofbit | strm::eofbit用来指出流到达了文件结束 |
s.eof() | 若流s的eofbit置位,则返回true |
s.fail() | 若流s的failbit或badbit置位,则返回true |
s.good() | 若流s处于有效状态,则返回true |
s.clear() | 将流s中所有条件状态位复位,将流的状态设置为有效,返回void |
s.clear(flags) | 根据给定的flags标志位,将流s中对应条件状态位复位。flags的类型为strm::iostate,返回void |
s.setstate(flags) | 根据给定的flags标志位,将流s中对应条件状态位置位。flags的类型为strm::iostate,返回void |
s.rdstate() | 返回流s的当前条件状态,返回值类型为strm::iostate |
8.1.3 管理输出缓冲
- 每个输出流都管理一个缓冲区,用来保存程序读写的数据。
- 导致缓冲刷新的原因:
- 程序正常结束,作为main函数的return操作的一部分,缓冲刷新被执行。
- 缓冲区满时,需要刷新缓冲,而后新的数据才能继续写入缓冲区。
- 我们可以使用操纵符如endl来显式刷新缓冲区。
- 在每个输出操作之后,我们可以用操纵符unitbuf设置流的内部状态,来清空缓冲区,默认情况下,对cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的。
- 一个输出流可能被关联到另一个流。在这种情况下,当读写被关联的流时,关联到的流的缓冲区会被刷新。例如,默认情况下,cin和cerr都关联到cout。因此,读cin或写cerr都会导致cout的缓冲区被刷新。
- 刷新输出缓冲区
cout << "hi!" << endl;
cout << "hi!" << flush;
cout << "hi!" << ends;
- 如果想在每次输出操作后都刷新缓冲区,我们可以使用unitbuf操纵符。
cout << unitbuf;
cout << nounitbuf;
- 当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流。
8.2 文件输入输出
- 头文件fstream定义了三个类型来支持文件IO:
- ifstream从一个给定文件读取数据。
- ofstream向一个给定文件写入数据。
- fstream可以读写给定文件。
函数与标记 | 用法 |
---|
fstream fstrm; | 创建一个未绑定的文件流,fstream是头文件fstream中定义的一个类型 |
fstream fstrm(s); | 创建一个fstream,并打开名为s的文件 |
fstream fstrm(s,mode) | 与前一个构造函数类似,但按指定mode打开文件 |
fstrm.open(s) | 打开名为s的文件,并将文件与fstrm绑定 |
fstrm.close() | 关闭与fstrm绑定的文件,返回void |
fstrm.is_open() | 返回一个bool值,指出与fstrm关联的文件是否成功打开且尚未关闭 |
8.2.1 使用文件流对象
- 当我们想要读写一个文件时,可以定义一个文件流对象,并将对象与文件关联起来。
ifstream in(ifile);
ofstream out;
- 接受一个iostream类型引用(或指针)参数的函数,可以用一个对应的fstream(或sstream)类型来调用。
- 成员函数open和close
ifstream in(ifile);
ofstream out;
out.open(ifile + ".copy");
if (out)
in.close();
in.open(ifile + "2");
8.2.2 文件模式
- 每个流都有一个关联的文件模式,用来指出如何使用文件。
文件模式 | 用法 |
---|
in | 以读方式打开 |
out | 以写方式打开 |
app | 每次写操作前均定位到文件末尾 |
ate | 打开文件后立即定位到文件末尾 |
trunc | 截断文件 |
binary | 以二进制方式进行IO |
- 指定文件模式有如下限制:
- 只可以对ofstream或fstream对象设定out模式。
- 只可以对ifstream或fstream对象设定in模式。
- 只有当out也被设定时才可设定trunc模式。
- 只要trunc没被设定,就可以设定app模式。在app模式下,即使没有显式指定 out模式,文件也总是以输出方式被打开。
- 默认情况下,即使我们没有指定trunc, 以out模式打开的文件也会被截断。为了保留以out模式打开的文件的内容,我们必须同时指定app模式,这样只会将数据追加写到文件末尾;或者同时指定in模式,即打开文件同时进行读写操作。
- ate和binary模式可用于任何类型的文件流对象,且可以与其他任何文件模式组合使用。
- 以out模式打开文件会丢弃已有数据。
ofstream out("file1");
ofstream out2("file1", ofstream::out);
ofstream out2("file1", ofstream::out | ofstream::trunc);
ofstream app("file2", ofstream::app);
ofstream app2("file2", ofstream::out | ofstream::app);
ofstream out;
out.open("scratchpad");
out.close();
out.open("precious", ofstream::app);
out.close();
8.3 string流
- sstream头文件定义了三个类型来支持内存IO:
- istringstream从string读取数据。
- ostringstream向string写入数据。
- stringstream既可从string读数据也可向string写数据。
函数与标记 | 用法 |
---|
sstream strm; | strm是一个未绑定的stringstram对象。sstream是头文件sstream中定义的一个类型。 |
sstream strm(s); | strm是一个sstream对象,保存strings的一个拷贝,此构造函数是explicit的。 |
strm.str() | 返回strm所保存的string的拷贝。 |
strm.str(s) | 将string s拷贝到strm中,返回void。 |
8.3.1 使用istringstream
- 当某些工作是对整行文本进行处理,而其他一些工作是处理行内的单个单词时,通常可以使用istringstream。
8.3.2 使用ostringstream
- 当我们逐步构造输出,希望最后一起打印时,ostringstream是很有用的。