前言
该系列是《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;
}