1 C++缓冲区
在学习标准IO库之前,我们先了解C++中缓冲区的使用。关于操作系统中缓冲区的学习与理解,请查看操作系统-缓存管理。
1.1 什么是缓冲区
缓冲区又称缓存,是内存空间的一部分。系统在内存中预留一部分用于缓冲输入输出的数据,缓冲区根据用途分为输入缓冲区和输出缓冲区两种。
1.2 为什么要引入缓冲区
C++中缓冲区的使用主要有以下几个目的:
- 缓和CPU与I/O设备间速度不匹配的矛盾。
- 减少对CPU的中断频率,放宽对CPU中断响应时间的限制。
- 提高CPU和I/O设备之间的并行性,让CPU可以处理其他工作
1.3 缓冲区类型
C++缓冲区分为全缓冲、行缓冲和无缓冲三种。
全缓冲
在此类缓冲中,当填满标准I/O缓冲后,才进行实际的I/O操作。典型的代表是磁盘文件的读写。行缓冲
在此类缓冲中,当在输入和输出中遇到换行符时,执行真正的I/O操作。输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。无缓冲
标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
1.4 缓冲区的刷新
下列情况将导致缓冲区刷新
程序正常结束
作为main函数return的一部分,缓冲刷新被执行。缓冲区满
需要刷新缓冲,新数据才可写入。使用操作符控制缓冲区刷新
endl:输出内容加换行,然后刷新
ends:输出内容加空字符,然后刷新
flush:输出内容后直接刷新用
unitbuf设置流的内部状态
cout << unitbuf; //所有输出操作后会立即刷新缓冲区
cout << <<nounitbuf; //回到正常的缓冲方式
- 一个输出流被关联到另一个流上
使用tie函数可以将两个流关联在一起。当读写被关联的流,被关联的流会刷新。cin和cerr都关联到cout,因此读cin或者cerr都会导致cout的缓冲区更新。我们可以将istream关联到ostream上,也可将ostream关联到另一个ostream上。不建议将cin关联到cerr上。
警告:如果程序崩溃了,则不会刷新缓冲区。
1.5 行缓冲演示
getchar()是行缓冲的,第一次调用getchar()函数,会让程序使用者输入一行字符并直至按下回车键函数才返回。此时用户输入的字符和回车符都存放在行缓冲区。再次调用getchar()函数,会逐步输出行缓冲区的内容。
int main(){
char c;
c = getchar();
cout << c << endl;
while ((c = getchar())!= '\n'){
cout << c << endl;
}
}
/*一次性输入 a b c
输出
a
b
c
*/
2 IO类
iostream中的类型和对象都是操作char数据的。默认情况下,这些对象都是关联到用户控制台的窗口的。
为了支持不同种类的IO处理操作,在istream和ostream之外,标准库还定义了一些其他的IO类型。iostream定义了用于读写流的基本类型,fstream定义了读写命名文件的类型,sstream定义了读写内存string对象的类型。

为了支持宽字符的语言,标准库定义了一组类型和对象来操作wchar_t类型的数据。宽字符版本的类型和函数的名字以一个w开始,与其普通char版本定义在同一个头文件中。
2.1 IO对象无拷贝
由于不能拷贝IO对象,因此我们不能将形参或返回类型设置为流类型。进行IO的函数通常以引用方式传递和返回流。读写一个IO对象会改变其状态,因此传递和返回的引用不能是const。
2.2 条件状态
IO类所定义的一些函数和与机器无关的iostate标志,可以帮助我们访问和操作流的条件状态。

1) 条件状态说明
上述任何一个IO对象在任意时刻都有一种状态,iostate代表状态的枚举,badbit,failbit,eofbit,goodbit是iostate的一个具体值。
操作good在所有错误位均未置位的情况下返回true。而badbit、failbit和eofbit任一被置位,检测流的状态条件会失败,此时fail操作返回true。bad、fail和eof只有在相应错误为被置位时才返回true。
一个流一旦发生错误,其上后续的IO操作都会失败。只有当一个流处于无错状态时,我们才可对它读写数据。通常用以下语句检查一个流在使用前的状态:
while (cin >> word)
//输入操作成功,流保持有效,条件状态为真
** 2) 条件状态管理**
流对象的rdstate成员返回一个iostate值,对应流的当前状态。setstate操作将给定条件为置位,表示发生了对应错误。
clear成员是一个重载函数,提供两个版本:clear无参数版本将复位所有错误标志位,带参数版本提供一个新的iostate状态。
2.3 管理输出缓冲
关于缓冲区内容本章开头有介绍。
3 文件输入输出
ifstream从一个给定的文件读取数据,ofstream向一个给定文件写入数据,fstream可以读写给定文件。
可以用getline函数从一个ifstream读取数据,除继承自iostream类型中行为外,fsteam还提供了一下成员来管理与流关联的文件。

3.1 使用文件流
当需要读写一个文件时,可以定义一个文件流对象,并将对象与文件关联起来。文件名可以用string类型,也可以用传统C风格字符串。
1) 成员函数open和close
如果我们定义了一个空文件流对象,可以随后用open将它与文件关联起来。如果调用open失败。failbit将会被置位。因此检测open是否成功是一个好习惯。
使用close函数可关闭文件流所绑定的文件。
string text;
ifstream fstm;
fstm.open("test.txt");
if (fstm){
getline(fstm, text);
cout << text;
fstm.close();
}
else{
cout << "failed";
}
\\如果顺利打开test.txt,条件为真,则从中读取内容,输出并关闭文件
\\打开失败则输出failed
2) 自动构造和析构
当一个fstream离开其作用域,close会自动被调用,与之关联的文件会自动关闭。
3.2 文件模式
在打开文件时,无论是调用 open还是以文件名作为流初始化的一部分,都需指定文件模式(file mode)。

只可以对
ofstream或fstream对象设定out模式只可以对
ifstream或fstream对象设定in模式只有打开
out模式,才可设定trunc模式只要
trunc模式未被设定,就可设定app模式。在app模式下,即使没有显示指定out模式,文件也总以输出模式被打开默认情况下,以
out模式打开文件,在位指定trunc时,也将被截断。为保留其中内容,可以使用app模式或者in模式(文件同时读写)ate和binary模式可用于任何类型的文件流对象且可以与其他模式组合使用。ate模式只在打开时有效:文件打开后将定位在文件尾。以binary模式打开的流则将文件以字节序列的形式处理,而不解释流中的字符。
默认情况下,当我们打开一个ofstream时,文件内容会被丢弃。对于用 ofstream 打开的文件,要保存文件中存在的数据,唯二方法是显式地指定app模式或 in 模式打开:
4 string流
sstream头文件中定义了以下三个类型来支持内存IO。
istringstream从string中读取数据,ostringstream向string中写入数据,而stringstream即可从string读数据也可以向string写数据。出了从iostream中继承的操作外,sstream中还支持以下操作:

4.1 sstream的使用
string line, word;
while (getline(cin, line)) {
istringstream stream(line);
while (stream >> word){
// do per-word processing
}
}
string中数据全部读出后,同样会触发“文件结束”信号,此时while循环条件为false。
4.2 转换或格式化
sstream类似于C语言风格中ssprintf,可以方便地构造各种风格的string。
string format_message = "cp 3 lbj 23";
istringstream input(format_message);
ostringstream output;
int val1 = 0, val2 = 0;
string str1, str2;
input >> str1 >> val1 >> str2 >> val2;
output << val1 << " " << val2 << endl;
cout << output.str();
