cppprimer第八章
第8章 IO库
8.1 IO类
IO 库类型定义在三个头文件中:
- iostream:定义了读写流的类型:istream, ostream, iostream, wistream, wostream, wiostream
- fstream:读写命名文件的类型:ifstream, ofstream, fstream, wifstream, wofstream, wfstream
- sstream:读写内存 string 对象的类型:istringstream, ostringstream, stringstream
其中带 w 前缀的类型用来操作宽字符语言(wchar_t)。宽字符版本的类型和函数前都有一个 w,如 wcin, wcout, wcerr。
fstream 和 sstream 中的类型都继承自 iostream 中的类型。
可以将一个派生类对象当成基类对象来使用。
8.1.1 IO对象无拷贝或赋值
不能拷贝或对 IO 对象赋值,因此也不能将形参或返回类型设置成流类型。
进行 IO 操作的函数通常以引用形式传递和返回流。读写 IO 对象会改变其状态,因此引用不能是 const 的。
8.1.2 条件状态
IO 操作的问题是可能发生错误。因此在使用一个流之前,应该先检查它是否处于良好状态。
条件状态用来查看流的状态。
IO 库的状态
1 | iostream::iostate;//作为位集合来使用,可以表达流的完整状态。通过位运算符可以一次性检测或设置多个标志位。 |
检查流的状态
1 | while(cin >> word);// >> 表达式返回流的状态 |
管理条件状态
cin.rdstate();//返回一个 iostate 值表示当前状态。 cin.setstate(state);//接受一个 iostate 类型的参数,将给定条件位置位。 cin.clear();//清除(复位)所有错误标志位,cin.clear() 后,cin.good() 会返回 true cin.clear(state);//接受一个 iostate 类型的参数,设为流的新状态。
设置某个标志位的方式
cin.clear(cin.rdstate() & ~cin.failbit);//将 failbit 复位
8.1.3 管理输出缓冲
每个输出流都管理一个缓冲区。
缓冲刷新(即数据真正写到设备或文件)的原因:
- 程序正常结束,比如执行到了 return。
- 缓冲区满了
- 使用操纵符如 endl, flush, ends 来显示刷新缓冲区
- 当读写被关联的流时,如读 cin 或写 cerr 都会刷新 cout 的缓冲区
- 使用操纵符 unitbuf 设置流的内部状态来清空缓冲区。
操纵符 endl, flush, ends
cout << a << endl;//输出 a 和一个换行,然后刷新缓冲区 cout << a << flush;//输出 a,然后刷新缓冲区 cout << a << ends;//输出 a 和一个空字符,然后刷新缓冲区
操纵符 unitbuf, nounitbuf
- unitbuf:告诉流接下来每次写操作之后都进行一次 flush 操作
- nounitbuf:重置流,恢复正常的刷新机制
cout << unitbuf; //后面的所有输出操作都会立即刷新缓冲区 cout << nounitbuf; //回到正常的缓冲方式
注意:如果程序异常终止,将不会刷新缓冲区,即此时相应的输出操作已执行但没有打印。
关联输入和输出流
当一个输入流关联到一个输出流,每次从该输入流读取数据前都会先刷新关联的输出流。
标准库将 cin 和 cout 关联在一起。
输入流的成员函数 tie 可以用来查看关联的输出流或关联到输出流:
cin.tie();//返回指向关联到 cin 的输出流的指针,如果没有关联的输出流,返回空指针。 cin.tie(&cerr);//接受一个指向输出流 cerr 的指针作为参数,将 cin 与 cerr 关联在一起 cin.tie(NULL);//cin 不再与其他流关联
每个流最多关联到一个输出流,但一个输出流可以被多个流关联。
8.2 文件输入输出
头文件 fstream 中定义的 ifstream, ofstream, fstream 类型用来对文件进行读写。
当要读写一个文件时,创建一个文件流对象并将之绑定到该文件。
8.2.1 使用文件流对象
C++11 中,文件名可以是 string 类型对象,也可以是 C 风格字符串
fstream 定义和初始化
fstream fs; // 创建一个未绑定的文件流 fs fstream fs('data.txt'); // 创建一个绑定到文件 data.txt 的文件流 fs,并打开文件 data.txt fstream fs('data.txt', mode); // 与上一个构造函数类似,但是按指定模式 mode 打开文件
fstream 特有操作
getline(ifs, s); // 从一个输入流 ifs 读取一行字符串存入 s 中 fs.open('data.ext'); // 将 fs 与文件 data.txt 绑定并打开该文件。如果已打开会发生错误。 fs.close(); // 关闭 fs 绑定的文件。 fs.is_open(); // 返回一个 bool 值,指出关联文件是否成功打开。
当定义了一个空的文件流对象,使用 open 函数将其与文件关联并打开文件。
如果 open 失败,failebit 会被置位,建议每次 open 后检测 open 是否成功。
不能对已打开的文件流调用 open。
当文件关闭后,可以将文件流关联到另一个文件。
当一个 fstream 对象被销毁时,close 函数会自动被调用。
用 fstream 代替 iostream
使用 iostream 类型的引用作为函数参数的地方,都可以使用 fstream 来代替。
8.2.2 文件模式
每次打开文件都以某种模式打开,如未指定即以该文件流类型的默认模式打开。
每个流都有一个关联的文件模式,用来指出如何使用文件
- in:以只读方式打开
- out:以只写方式打开
- app:每次写操作前均定位到文件末尾
- ate:打开文件后即定位到文件末尾
- trunc:截断文件
- binary:以二进制方式进行 IO
文件模式的使用:
- 每个流对象都有默认的文件模式,ifstream 默认 in 模式打开文件,ofstream 默认 out,fstream 默认 in 和 out。
- 对 ifstream 对象不能设置 out 模式,对 ofstream 对象不能设置 in 模式
- 只有设置了 out 才能设置 trunc 模式,只设置 out 模式会默认也设置 trunc 模式
- 设置了 trunc 就不能再设置 app 模式
- 默认情况下以 out 模式打开文件会使文件内容被清空,如果要保留文件内容需要同时指定 app 模式或 in 模式。
- app 模式下,会将写入的数据追加写到文件末尾
ofstream fout("file1.txt"); // 以输出模式打开文件并截断文件(即清空文件内容) ofstream fout("file1.txt", ofstream::app); // 显示指定 app 模式(+隐含的 out 模式) ofstream fout("file1.txt", ofstream::app | ofstream::out); // 同上,只是将 out 模式显式地指定了一下。 fout.open("file1.txt", ofstream::out);
8.3 string流
sstream 定义了 istringstream, ostringstream, stringstream 来读写 string。
sstream 定义和初始化
stringstream strm(); // 定义一个未绑定的 stringstream 对象 stringstream strm(s); // 定义一个 stringstream 对象 strm,strm 中保存着 string s 的拷贝。
8.3.1 使用istringstream
stringstream 特有操作
strm.str(); // 返回 strm 中保存的 str 的拷贝 strm.str(s); // 将 string s 拷贝到 strm 中,返回 void
8.3.2 使用ostringstream
理解:
- istringstream 是输入流,即读操作,要将流中的内容输入到字符串中,因此定义和使用 istringstream 时流内必须有内容,所以在使用前要提前在流内保存一个字符串
- ostringstream 是输出流,即写操作,将流中的内容输出到字符串中,ostringstream 可以在定义时即在流中保存一个字符串,也可以通过 << 操作符获得字符串。