cppprimer第八章

第8章 IO库

8.1 IO类

IO 库类型定义在三个头文件中:

  1. iostream:定义了读写流的类型:istream, ostream, iostream, wistream, wostream, wiostream
  2. fstream:读写命名文件的类型:ifstream, ofstream, fstream, wifstream, wofstream, wfstream
  3. 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
2
3
4
5
iostream::iostate;//作为位集合来使用,可以表达流的完整状态。通过位运算符可以一次性检测或设置多个标志位。
iostream::badbit; cin.badbit;//表示流已崩溃,是系统及错误或不可恢复的读写错误。流无法再使用。
iostream::failbit; cin.failbit;//表示一个 IO 操作失败了,是可恢复错误。修正后流可以继续使用。
iostream::eofbit; cin.eofbit;//表示流到达了文件结束
iostream::goodbit;//表示流未处于错误状态

检查流的状态

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 管理输出缓冲

每个输出流都管理一个缓冲区。

缓冲刷新(即数据真正写到设备或文件)的原因:

  1. 程序正常结束,比如执行到了 return。
  2. 缓冲区满了
  3. 使用操纵符如 endl, flush, ends 来显示刷新缓冲区
  4. 当读写被关联的流时,如读 cin 或写 cerr 都会刷新 cout 的缓冲区
  5. 使用操纵符 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

理解:

  1. istringstream 是输入流,即读操作,要将流中的内容输入到字符串中,因此定义和使用 istringstream 时流内必须有内容,所以在使用前要提前在流内保存一个字符串
  2. ostringstream 是输出流,即写操作,将流中的内容输出到字符串中,ostringstream 可以在定义时即在流中保存一个字符串,也可以通过 << 操作符获得字符串。