前言
该系列是《C++Primer第五版》的笔记,包含本人认为值得记录和整理的主要的知识点,并不是全部内容,也不是具体的内容。
该系列文章的作用应该是作为复习或预习的参考,有哪些知识点忘记或想学,可以大致浏览下该文章,然后再去书中寻找详细解答。(本系列文章基本是按书本顺序罗列的知识点,便于大家去书中寻找)
所以看该文章前,需要有一定的C++基础,否则阅读起来可能有困难。
本文大致整理了第八章的知识点,涉及到C++关于IO库的知识,内容不多,也不算很重要。
链接目录
- 第二章:变量与基本类型
- 第三章:字符串、向量和数组
- 第四章:表达式
- 第五章:语句
- 第六章:函数
- 第七章:类
- 第八章:IO库
- 第九章:顺序容器
- 第十章:泛型算法
- 第十一章:关联容器
- 第十二章:动态内存
- 第十三章:拷贝控制
- 第十四章:重载运算与类型转换
- 第十五章:面向对象程序设计
- 第十六章:模板与泛型编程
- 第十七章:标准库特殊设施
- 第十八章:用于大型程序的工具
- 第十九章:特殊工具与技术
IO类
头文件:
iostream:istream、wistream从流读取数据ostream、wostream向流写入数据iostream、wiostream读写流
fstream:ifstream、wifstream从文件读取数据ofstream、wofstream向文件写入数据fstream、wfstream读写文件
sstream:istringstream、wistringstream从string读取数据ostringstream、wostringstream向string写入数据stringstream、wstringstream读写string
IO类型间的关系:标准库通过继承机制可以忽略不同类型之间的流的差异,这些流特性可以无差别地应用于普通流、文件流和string流,以及char或宽字符流版本。
IO对象无拷贝或赋值
IO对象不可拷贝或赋值,所以在函数中,只能以引用地方式传递和返回流,读写IO流回改变其状态,所以传递和返回的引用也不能是const的。
条件状态
IO库条件状态:

//IO错误的例子
int val;
cin >> val;
//当输入不是数值型,读操作就会失败,cin进入错误状态
//可以将流当作条件,当输入成功时,流保持有效状态,条件为真
while(cin >> word){
...
}
查询流的状态:相比流作为条件,可以更确切地知道是哪种错误。
错误类型:
badbit:系统级错误failbit:可恢复错误eofbit:到达结束位置goodbit:0表示未发生错误
badbit表示系统级错误,如不可恢复的读写错误。通常发生该错误流就无法再使用了。
failbit通常是发生可恢复错误,如期望读取数值却读出一个字符,可以修正,流还可以继续使用。
到达文件结束位置,eofbit和failbit都会被置位。
goodbit为0表示流未发生错误,如果badbit、failbit和eofbit任一个被置位,检测流状态的条件都会失败。
管理条件状态:流对象的rdstate返回一个iostate值,表示流当前状态。clear函数不接受参数复位所有错误标志位,带参数则复位对应的状态。
auto old_state = cin.rdstate();//保存当前流状态
cin.clear();//清除流状态
process_input(cin);//使用cin,可能改变流状态
cin.setstate(old_state);//将cin置为原有状态
//复位failbit和badbit,其他标志位不变
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit);
管理输出缓冲
缓冲刷新(数据真正写到输出设备或文件)的原因:
- 程序正常结束。
- 缓冲区满,需要刷新缓冲,才能继续写。
- 使用操作符(如
endl)显式刷新。 - 输出操作之后,使用操纵符
unitbuf设置流的内部状态清除缓冲区。默认情况下,cerr是设置unitbuf的,所以写到cerr的内容都是立即刷新的。 - 一个输出流可能会被关联到另一个流,当读写被关联的流时,关联到的流的缓冲区会被刷新。默认情况下,
cin和cerr都关联到cout,所以读cin或写cerr都会导致cout的缓冲区刷新。
刷新输出到缓冲区:
endl,输出额外一个换行符,刷新缓冲区。flush,刷新缓冲区,不附加任何额外字符。ends,输出额外一个空字符,刷新缓冲区。
cout << "hi" << endl;//输出hi和一个换行,然后刷新缓冲区
cout << "hi" << flush;//输出hi,然后刷新缓冲区
cout << "hi" << ends;//输出hi和一个空字符,然后刷新缓冲区
unitbuf操纵符:如果想在每次输出操作后都刷新缓冲区,可以使用unitbuf操纵符。
cout << unitbuf;//所有输出操作后都会立即刷新缓冲区
cout << nounitbuf;//回到正常的缓冲方式
警告:如果程序崩溃,输出缓冲区不会被刷新
如果程序异常终止,输出缓冲区不会被刷新,数据可能停留在缓冲区等待打印,会导致一些调试信息没有输出。
关联输入和输出流:当一个输入流被关联到一个输出流时,从输入流读数据都会先刷新关联的输出流。
cin >> ival;//会导致cout的缓冲区被刷新
//tie函数返回一个ostream的引用,有无参和有参两种形式
ostream* tie ( ) const; //返回指向绑定的输出流的指针。
ostream* tie ( ostream* tiestr ); //将tiestr指向的输出流绑定的该对象上,并返回上一个绑定的输出流指针。
x.tie(&o);//将流x关联到输出流o
cin.tie(&cout);//标准库默认将cin和cout关联
ostream *old_tie = cin.tie(nullptr);//cin不与其他流关联
cin.tie(&cerr);//读取cin会刷新cerr
cin.tie(old_tie);//重建默认关联
每个流同时最多关联到一个流,但多个流可以同时关联到一个ostream。
文件输入输出
头文件fstream定义了三种类型:
ifstream:从文件读取数据。ofstream:向文件写入数据。fstream:可以对文件进行读写数据。
fstream继承iostream,除了iostream的操作外,还有特有的操作。

使用文件流对象
用fstream代替iostream:和cin和cout一样可以使用<<和>>操作符。
ifstream input(s1);//打开要读入的文件
ofstream output(s2);//打开要写入的文件
//读取文件并写入文件
int i;
while(input >> i){
output << i << endl;//s1文件中每个元素按行写入s2
}
成员函数open和close:
ifstream in(ifile);//带参构造函数默认执行open
ofstream out;
out.open(ifile + ".copy");//显式执行open打开指定文件
//如果调用open失败,failbit会被置位
if(out)///检查open是否成功
另外对一个已经打开的文件流调用open会失败,导致failbit被置位,随后试图使用文件流的操作都会失败。为了将文件流关联到另一个文件,必须先关闭。
in.close();
in.open(ifile2);
自动构造和析构:考虑如下情形,每个循环步构造一个新的ifstream对象,并打开一个文件。因为input是循环的局部变量,每次循环步结束后都会进行销毁,当ftream对象离开作用域时(销毁时),与之关联的文件会自动关闭。
while(...){
ifstream input(s);
...//没有调用close
}
文件模式

指定文件模式有如下限制:
- 只可以对
ofstream或fstream对象设定out模式。 - 只可以对
ifstream或fstream对象设定in模式。 - 只有当
out也被设定时才可以设定trunc模式。 - 只要
trunc没被设定,就可以设定app模式。 - 默认情况以
trunc方式进行写,如果要保留原有文件内容,则必须指定app模式。 ate和binarr可以与任何其他模式组合。
ifstream关联的文件默认以in打开,ofstream关联的文件默认以out打开。
以out模式打开文件会丢弃已有数据:
//为了保留文件内容,必须显式指定app模式
ofstream out(file, ofstream::app);
ofstream out(file, ofstream::out | ofstream::app);
每次调用open时都会确定文件模式:每次打开文件都可以改变文件模式。
ofstream out;
out.open(file);
out.close();
out.open(file2, ofstream::app);
out.close();
string流
sstream头文件定义了三种类型支持内存IO(向string读写数据):
istringstream:从string读取数据。ostringstream:向string写入数据。stringstream:可以对string进行读写。
sstream继承自iostream,除了继承来的操作,还额外有:

使用istringstream
//对于如下数据:包含一个人名和不定数量的数字
//morgan 2015523 862546
string line, word;
while(getline(cin, line)){
istringstream record(line);
record >> info.name;//将第一个名字保存
while(record >> word)//将所有号码保存,直到行尾
record >> info.number;//假设info.number能正确存下所有号码
}
使用ostringstream
有时并不是一次性能够完成输出到文件的内容的处理,可以先写入到ostringstream中,等操作全部完成后再写入到文件。
ostringstream rightNums, badNums;//存储正确的或不正确的号码
if(valid(nums)){
rightNums << " " << nums;
}else{
badNums << " " << nums;
}