《C++Primer 第五版》
——读书随笔集
第八章
8.1 IO类
- istream,(输入流)类型,提供输入操作
- ostream ,(输出流)类型,提供输出操作
- cin,一个istream对象,从标准输入读取数据‘
- cout,一个ostream对象,向标准输出写入数据
- cerr,一个ostream对象,通常用于输出程序错误消息,写入到标准错误
- <<运算符,用来向一个ostream对象中写入输出数据
- />>运算符,用来从一个istream对象中读取输入数据
8.1.1 IO对象无拷贝和赋值
我们不能拷贝或对IO对象赋值,所以我们也不能将形参或返回类型设置为流类型。进行IO操作的函数通常以引用方式传递和返回流。读写一个IO对象会改变其状态,因此传递和返回的引用不能是const。
ofstream out1, out2;
out1 = out2; //错误:不能对流对象赋值
ofstream print(ofstream); //错误:不能初始化ofstream对象
out2 = print(out2); //错误,不能拷贝流对象
头文件 | 类型 |
---|---|
iostream | istream,wistream从流中读取数据 |
ostream,wostream向流中写入数据 | |
iostream,wiostream读写流 | |
fstream | ifstream,wifstream从文件中读取数据 |
ofstream,wofstream向文件中写入数据 | |
fstream,wfstream读写文件 | |
sstream | istringstream,wistringstream从string中读取数据 |
ostringstream,wostringstream向string中写入数据 | |
stringstream,wstringstream读写string |
为了支持宽类型,标准库定义了一组类型和对象来wchar_t类型的数据。宽字符版本的类型和函数的名字以一个w开始。
本文介绍的标准库流特性都可以无差别地应用于普通流,文件流和string流,以及char或宽字符版本
8.1.2 条件状态
IO类定义了一些函数和标志,可以帮助我们访问和操纵流的条件状态
strm::iostate | strm是一种IO类型,iostate是一种机器相关的类型,提供了表达条件状态的完整功能 |
---|---|
strm::badbit | strm::badbit指流已崩溃 |
strm::failbit | strm::failbit用来指一个IO操作已经失败 |
strm::eofbit | strm::eofbit用来指出流到达了文件结束 |
strm::goodbit | strm::goodbit用来指出流未处于错误状态。此值保证为零 |
s.eof() | 若流s的eofbit置位,则返回true |
s.fail() | 若流s的failbit或badbit置位,则返回true |
s.bad() | 若流s的badbit置位,则返回true |
s.good() | 若流s处于有效状态,则返回true |
s.clear(flags) | 根据给定的flags标志位,将流中对应条件状态位复位。flags的类型为strm::iostate。返回void |
s.setstate(flags) | 根据给定的flags标志位,将流中对应条件状态位置位。flags的类型为strm::iostate。返回void |
s.rdstate() | 返回流s的当前条件状态,返回值类型为strm::iostate |
-
查询流的状态
badbit表示系统级错误,如不可恢复的错误。通常情况下,一旦badbit被置位,那么流就无法使用了。
failbit被置位,如期望读取一个数值,结果读取了一个字符。这种问题是可以修复的,流还能继续使用。
如果到达文件结束位置,eofbit和failbit就会被置位。goodbit的值为0,表示流还未发生错误。
如果failbit,eofbit和badbit任一个被置位,则检测流状态的条件就会失败。
标准库还提供了一组函数来查询这些标志位的状态。操作good在所有错误位均未被置位的情况下返回true。
-
管理条件状态
clear函数成员是一个重载的成员。他有一个接受参数的版本和一个不接受参数的版本。不接收参数的版本清除所有错误标志位。执行clear()后,调用good会返回true。
auot old_stat = cin.rdstate(); //返回原来的旧参数 cin.clear(); //清除参数 process(cin); // 使用cin cin.setstate(old_state); //复原原来的cin
带参数的clear版本接受一个iostate值,表示流的新状态。为了复位单一的状态位,我们可以首先用rdstate读出当前条件状态,然后用位操作将所需位复位来生成新的状态。例如:
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit) //将failbit和badbit复位,其它位保持不变。
8.1.3 管理输出缓冲
每个输出流都有一个缓冲区,用来保存程序读写的数据。
-
刷新输出缓冲区
可以使用操纵符endl来完成换行并刷新缓冲区。
可以使用flush刷新缓冲区,但不输出任何额外的字符。
可以使用ends向缓冲区插入一个空字符,然后刷新缓冲区。
-
unitbuf操纵符
使用unitbuf可以在每次输出操作后,都刷新缓冲区,它告诉流在接下来的每次写操作之后都进行一次flush操作。而nounitbuf操纵符则重置流,使其恢复使用系统正常的缓冲区刷新机制。
cout<<unitbuf; // 所有输出操作后立即刷新缓冲区 cout<< nounitbuf; //回到正常的缓冲方式
-
关联输入和输出流
当一个输入流被关联到一个输出流时,任何一个试图从输入流读取数据的操作都会先刷新关联的输出流。标准库将cout和cin关联在一起
cin>>ival; //将刷新cout的缓冲区
交互式系统通常应该关联输入流和输出流。这意味着用户在输入之前,所有输出包括提示信息都应该被输出。
tie函数有两个重载的版本:一个不带参数(返回指向输出流的指针)。如果本对象当前关联到一个输出流,则返回的就是指向这个流的指针,如果对象未关联到流,则返回空指针。tie的第二个版本接受收一个指向ostream的指针,将自己关联到另一个ostream。
cin.tie(&cout); //仅仅用来展示 ostream *old_tie = cin.tie(nullptr);//cin不再与其它流关联 cin.tie(&cerr); //读取cin会刷新cerr cin.tie(&old_tie);//重建cin和cout之间的联系
每个流同时最多关联到一个流,但是多个流可以关联到同一个ostream
8.2 文件的输入输出
头文件fstream定义了三个类型来支持文件IO:ifstream,ofstream,fstream。
这些类型提供的操作与我们之前已经使用过的对象cin和cout一样。它除了继承来自iostream类型的行为以外,fstream中定义的类型还新增了成员来管理与流关联的文件。这些成员只能对ifstream,ofstream,fstream对象调用这些操作,但不能对其他IO类型调用这些操作。
fstream fstrm | 创建一个未绑定的文件流 |
---|---|
fstream fstrm(s) | 创建一个fstream,并打开名为s的文件。 |
fstream fstream(s,mode) | 与前一个构造函数类型,但按指定mode打开文件 |
fstrm.open(s) | 打开名为s的文件,并将文件与fstrm绑定。返回void |
fstrm.close() | 关闭与fstrm绑定的文件。返回void |
fstrm.is_open() | 返回一个bool值,指出文件是否打开且尚未关闭。 |
8.2.1使用文件流对象
ifstream in(file); //构建一个ifstream并打开给定文件
ofstream out; //输出流未关联到任何文件
文件名可以是一个库类型string对象,也可以是C风格字符数组
-
用fstream代替iostream&
在要求使用基类型对象的地方,我们可以用继承类型的对象来代替。
-
成员函数open和close
ifstream in(ifile); //构建一个ifstream并打开文件 ofstream out; //输出文件流未与任何文件关联 out.open(ifile + ".copy"); //打开指定文件
-
自动构造和析构
当一个fstream对象被销毁时,close会自动被调用
8.2.2 文件模式
每个流都有一个文件模式,用来指出如何使用文件。
in | 以读方式打开 |
---|---|
out | 以写方式打开 |
app | 每次写操作前均定位到文件末尾 |
ate | 打开文件后立即定位到文件末尾 |
trunc | 截断文件 |
binary | 以二进制方式进行IO |
无论用哪种方式打开文件,我们都可以指定文件模式,调用open打开文件时可以,用一个文件名初始化流来隐式打开文件时也可以。指定文件模式有如下限制∶
- 只可以对 ofstream 或 fstream 对象设定 out 模式。
- 只可以对ifstream 或 fstream 对象设定 in 模式。
- 只有当 out 也被设定时才可设定 trunc 模式。
- 只要trunc没被设定,就可以设定 app 模式。在app 模式下,即使没有显式指定 out 模式,文件也总是以输出方式被打开。
- 默认情况下,即使我们没有指定trunc,以out 模式打开的文件也会被截断。为了保留以out 模式打开的文件的内容,我们必须同时指定 app 模式,这样只会将数据追加写到文件末尾;或者同时指定in 模式,即打开文件同时进行读写操作。
- ate和binary 模式可用于任何类型的文件流对象,且可以与其他任何文件模式组合使用。
每个文件流类型都定义了一个默认的文件模式,当我们未指定文件模式时,就使用此默认模式。与ifstream关联的文件默认以in 模式打开;与 ofstream关联的文件默认以 out 模式打开;与fstream关联的文件默认以in 和 out 模式打开。
以out模式打开文件会丢失已有数据
默认情况下,当我们打开一个 ofstream 时,文件的内容会被丢弃。阻止一个 ofstream 清空给定文件内容的方法是同时指定 app 模式∶
// 在这几条语句中file1都被截断
ofstream out("file1"); // 隐含以输出模式打开文件并截断文件
ofstream out2("file1", ofstream::out); // 隐含地截断文件
ofstream out3("file1", ofstream::out | ofstream::trunc);
// 为保留文件内容,我们必须显式指定app模式
ofstream app("file2", ofstream::app); // 隐含为输出模式
ofstream app2("file2", ofstream::out | ofstream::app);
每次调用open时都会确定文件模式
对于一个给定的流,每次打开文件时,都可以改变文件模式
ofstream out; // 未指定文件打开模式
out.open("scratchpad"); // 模式隐含地设置为输出和截断
out.close(); // 关闭out,以便用于其他文件
out.open("precious", ofstream::app); // 模式为输出和追加
out.close();
第一个open调用未显式指定输出模式,文件隐式地以out 模式打开。
通常情况下,out模式意味着同时使用trunc模式。因此,当前目录下名为scratchpad的文件的内容将被清空。
当打开名为 precious 的文件时,我们指定了append模式。文件中已有的数据都得以保留,所有写操作都在文件末尾进行。
8.3 string流
sstream头文件定义了三个类型来支持内存IO,这些类型可以向string写入数据,也可以从string读取数据,就像string是一个IO流一样。
istringstream从string读取数据,ostringstream向string写入数据,而头文件stringstream都可以。下列是sstream特有的成员。
sstream strm | strm是一个未绑定的stringstream对象 |
---|---|
sstream strm(s) | strm是一个sstream对象,保存了string s的一个拷贝 |
strm.str() | 返回strm所保存的string的拷贝,返回的是个string |
strm.str(s) | 将string s拷贝到strm中。返回void |