C++Primer笔记-IO库

前言

该系列是《C++Primer第五版》的笔记,包含本人认为值得记录和整理的主要的知识点,并不是全部内容,也不是具体的内容。
该系列文章的作用应该是作为复习或预习的参考,有哪些知识点忘记或想学,可以大致浏览下该文章,然后再去书中寻找详细解答。(本系列文章基本是按书本顺序罗列的知识点,便于大家去书中寻找)
所以看该文章前,需要有一定的C++基础,否则阅读起来可能有困难。

本文大致整理了第八章的知识点,涉及到C++关于IO库的知识,内容不多,也不算很重要。

链接目录

IO类

头文件:

  • iostream
    • istreamwistream从流读取数据
    • ostreamwostream向流写入数据
    • iostreamwiostream读写流
  • fstream
    • ifstreamwifstream从文件读取数据
    • ofstreamwofstream向文件写入数据
    • fstreamwfstream读写文件
  • sstream
    • istringstreamwistringstreamstring读取数据
    • ostringstreamwostringstreamstring写入数据
    • stringstreamwstringstream读写string

IO类型间的关系:标准库通过继承机制可以忽略不同类型之间的流的差异,这些流特性可以无差别地应用于普通流、文件流和string流,以及char或宽字符流版本。

IO对象无拷贝或赋值

IO对象不可拷贝或赋值,所以在函数中,只能以引用地方式传递和返回流,读写IO流回改变其状态,所以传递和返回的引用也不能是const的。

条件状态

IO库条件状态:

图片.png

//IO错误的例子
int val;
cin >> val;
//当输入不是数值型,读操作就会失败,cin进入错误状态
//可以将流当作条件,当输入成功时,流保持有效状态,条件为真
while(cin >> word){
	...
}

查询流的状态:相比流作为条件,可以更确切地知道是哪种错误。
错误类型:

  • badbit:系统级错误
  • failbit:可恢复错误
  • eofbit:到达结束位置
  • goodbit:0表示未发生错误

badbit表示系统级错误,如不可恢复的读写错误。通常发生该错误流就无法再使用了。
failbit通常是发生可恢复错误,如期望读取数值却读出一个字符,可以修正,流还可以继续使用。
到达文件结束位置,eofbitfailbit都会被置位。
goodbit为0表示流未发生错误,如果badbitfailbiteofbit任一个被置位,检测流状态的条件都会失败。

管理条件状态:流对象的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的内容都是立即刷新的。
  • 一个输出流可能会被关联到另一个流,当读写被关联的流时,关联到的流的缓冲区会被刷新。默认情况下,cincerr都关联到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的操作外,还有特有的操作。

图片.png

使用文件流对象

fstream代替iostream:和cincout一样可以使用<<>>操作符。

ifstream input(s1);//打开要读入的文件
ofstream output(s2);//打开要写入的文件
//读取文件并写入文件
int i;
while(input >> i){
	output << i << endl;//s1文件中每个元素按行写入s2
}

成员函数openclose

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
}

文件模式

图片.png

指定文件模式有如下限制:

  • 只可以对ofstreamfstream对象设定out模式。
  • 只可以对ifstreamfstream对象设定in模式。
  • 只有当out也被设定时才可以设定trunc模式。
  • 只要trunc没被设定,就可以设定app模式。
  • 默认情况以trunc方式进行写,如果要保留原有文件内容,则必须指定app模式。
  • atebinarr可以与任何其他模式组合。

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,除了继承来的操作,还额外有:

图片.png

使用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;
}
上一篇 下一篇

评论 | 0条评论