Tips for C++ Primer Chapter 8 IO库

第8章 IO库

IO类

IO库类型和头文件

头文件:类

iostream(控制台的IO):istream ostream iostream

fstream(命名文件的IO):ifstream ofstream fstream

sstream(内存string对象的IO):istringstream ostringstream stringstream

PS:

ifstream、istringstream继承自istream;

ofstream、ostringstream继承自ostream;

通常可以将一个派生类对象当作其基类对象来使用,例如可以对ifstream对象使用IO运算符(>>和<<)或getline等。

IO对象无拷贝和赋值

由于不能拷贝IO对象,因此不能将形参或返回类型设置为流类型;

进行IO操作的函数通常以引用的方式传递和返回流;

读写一个IO对象会改变其状态,因此传递和返回的引用不能是const的

条件状态

IO类定义了一些函数和标志,以访问和操纵流的条件状态(condition state)

以下strm表示某种IO类型(istream等),s是某个流对象

strm::iostate   iostate是一种机器相关的类型,提供了表达条件状态的完整功能

strm::badbit   用来指出流已崩溃

strm::failbit   用来指出一个IO操作失败

strm::eofbit   用来指出流达到了文件结束

strm::goodbit   用来指出流未处于错误状态(此值保证为0)

s.eof()   若流s的eofbit置位,则返回true

s.bad()   若流s的badbit置位,则返回true

s.good()   若流s处于有效状态,则返回true

s.clear()   将流s中所有条件状态位复位,将流的状态设置为有效(goodbit也复位了);返回void

s.clear(flags)   根据给定的flags标志位,将流s中对应条件状态位复位;flags的类型为strm::iostate;返回void

s.setstate(flags)   根据给定的flags标志位,将流s中对应条件状态位置位;flags的类型为strm::iostate;返回void

s.rdstate()   返回流s的当前条件状态,返回值类型为strm::iostate

流的条件状态位

badbit表示系统级错误,如不可恢复的读写错误。一旦badbit被置位流就无法再使用了;

当badbit被置位时,或者在发生可恢复错误后,failbit会被置位,如期望读取数值却读到一个字符等错误。这种错误可以修正,流还可以继续使用;

如果达到文件结束位置,eofbit和failbit都被置位;

当流未发生错误,goodbit保持为0

如果badbit、failbit、eofbit任一被置位,则检测流状态的条件会失败;

  例如 while(cin>>val) 在读到文件结束时终止循环。

流的条件状态查询函数

所有错误位均未置位时,good返回true(注意:此时goodbit为0而不是1);

bad、fail、eof在特定的对应错误位被置位时返回true(注意:badbit或eofbit被置位时,failbit也会被置位,所以fail也会返回true);

可见:good或fail是确定流的总体状态的方法。

  例如

  实际上 while(cin>>val)

  就等价于 while(cin>>val && cin.good())

  或 while(cin>>val && !cin.fail())

管理输出缓冲

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

由于设备的写操作可能很耗时,允许操作系统将多个输出操作暂存在缓冲区,然后组合为单一的设备写操作,可以带来很大的性能提升

缓冲区刷新即:数据真正写到输出设备或文件

导致缓冲区刷新的原因:

  程序正常结束;(实际上,缓冲刷新是main函数return操作的一部分;程序异常终止时输出缓冲区不会被刷新)

  缓冲区满;

  使用操纵符endl显式刷新缓冲区;

  写到cerr;(在每个输出操作之后,可以用操纵符unitbuf设置流的内部状态,来清空缓冲区;而默认情况下,对流对象cerr是设置unitbuf的,因此写到cerr的内容都是立即刷新的)

  一个输出流被关联到了另一个流的情况;(例如,默认情况下,cin和cerr都关联到cout,因此,读cin写cerr都会导致cout的缓冲区被刷新)

刷新输出缓冲区

以下操纵符需出现在某个输出操作之后。(cout、cerr等)

endl:换行并刷新缓冲区

flush:仅刷新缓冲区

ends:向缓冲区插入一个空格符,再刷新缓冲区

unitbuf操纵符

如果想在每次输出操作后系统都自动刷新缓冲区,可以使用unitbuf操纵符;

unitbuf告诉流在接下来的每次写操作之后都自动进行一次flush操作。

  cout << unitbuf;

  cout << nounitbuf; //回到正常的缓冲方式

关联输入和输出流

当一个输入流被关联到一个输出流时,使用该输入流的操作时会先刷新关联的输出流

标准库将cin与cout关联在一起。

tie函数

tie函数有两个重载的版本:

一个版本不带参数返回指向输出流的指针。如果本对象当前关联到一个输出流,则返回指向该输出流的指针;如果本对象未关联到流,则返回空指针

另一个版本接受一个指向ostream的指针,将自己关联到此ostream

例如:

  ostream* old_tie = cin.tie(); //old_tie指向当前关联到cin的流(系统默认是cout)

  cin.tie(&cerr); //将cin与cerr关联

  cin.tie(nullptr); //解除cin与任何流的关联

  cin.tie(old_tie); //重建cin与cout的关联

PS:

可以将一个istream关联到一个ostream,也可以将一个ostream关联到另一个ostream;

每个流最多关联到一个流,多个流可以同时关联到同一个ostream。

文件输入输出

头文件fstream定义了三个类型来支持文件IO:

  ifstream:读文件

  ofstream:写文件

  fstream:读写文件(将在第17章讨论)

除了继承自iostream类型的>>、<<、getline等行为之外,fstream中定义的类型有其特有的管理与流关联的文件的成员。

fstream特有的操作:

fstream fs   创建一个未绑定的文件流;fstream是头文件fstream中定义的一种类型

fstream fs(s)   创建一个fstream,并打开(此时open会自动被调用)名为s的文件;s可以是string类型或者指向C风格字符串的指针(C++11之前只允许C风格的字符数组);这些构造函数数都是explicit(第7章)的;默认的文件模式mode依赖于fstream的类型

fstream fs(s, mode)   与前一个构造函数类似,但按指定mode打开文件

fs.open(s)   打开名为s的文件,并将文件与fs绑定;默认的文件mode依赖于fstream的类型;返回void

fs.close()   关闭与fs绑定的文件;返回void

fs.is_open()   返回bool值;指出与fs关联的文件是否成功打开尚且未关闭

用fstream代替iostream&

在要求使用基类对象的地方,我们可以用继承类型的对象来替代;

这意味着,接受一个iostream类型引用(或指针)参数的函数,可以用一个对应的fstream(或sstream)类型来调用;

例如,如果有一个函数接受一个ostream&参数,调用这个函数时,可以传给它一个ofstream对象;(isstream&和ifstream也类似)

成员函数open和close

如果调用open失败,failbit会被置位;

fs.open(s);

if(fs) //如果open失败,则条件为假

对一个已经打开的文件流调用open会失败,并导致failbit被置位,随后的文件流操作也都会失败;为了将文件流关联到另一个文件,必须首先关闭已关联的文件。

fs.close();

若open成功,则open会设置流的状态,使得good()为true。

当一个fstream对象被销毁时,close会自动被调用。

文件模式(file mode)

in   读

out   写

app   每次写操作前均定位到文件尾

ate   打开文件后立即定位到文件尾

trunc   截断文件

binary   以二进制方式进行IO

文件模式规则:

(1)只可对ofstream或fstream对象设定out模式;

(2)只可对ifstream或fstream对象设定in模式;

(3)只有当out被设定时才可设定trunc模式;

(4)只要trunc未被设定,就可以设定app模式;在app模式下,即使没有显式设定out,文件也总是以输出方式被打开;

(5)默认情况下,即使未设定trunc,以out模式打开的文件也会被截断;为了保留以out模式打开的文件内容,必须同时指定app模式,这样就会将数据追加到文件尾;或者同时指定in模式,同时进行读写操作(第17章)

(6)ate和binary模式可用于任何类型的文件流对象,且可与其它任何文件模式组合使用;

当未显式指定文件模式时,各文件流类型的默认模式

ifstream:in

ofstream:out

fstream:in和out

一些例子:

ofstream out(s1); //默认为out模式,文件被截断

ofstream out2(s1, ofstream::out); //显式指定out,会隐式地截断文件(参考“文件模式规则”:第5条)

ofstream out3(s1, ofstream::out | ofstream::trunc); //显式截断文件(只有out也被设定,trunc才能被设定;参考“文件模式规则”:第3条

ofstream app(s2,ofstream::app); //隐式地设定了out

ofstream app2(s2,ofstream::out | ofstream::app); //与上一条等价

PS:以上是在初始化流并隐式打开文件时指定文件模式,此外在使用open打开文件时也同样可以指定文件模式。

string流

sstream头文件定义了三个类型来支持内存IO:

istringstream:从string读取数据

ostringstream:向string写入数据

stringstream:可从string读取数据,或向string写入数据

除了继承自iostream头文件的>>、<<、getline等操作,sstream头文件定义的类有其特有操作:

sstream st   st是一个未绑定的stringstream对象;sstream是头文件sstream中定义的某个类型

sstream st(s)   st是一个sstream对象,保存string s的一个拷贝;此构造函数时explicit(第7章)

st.str()   返回st所保存的string的拷贝

st.str(s)   将string s拷贝到st中;返回void

使用istringstream

例子:

string line, word;
while(getline(cin, line)) //读取一行
{
    istringstream iss(line); //将string流iss绑定到读入的一行line(流对象iss中存有line的一个拷贝)
    while(iss>>word) //使用输入运算符从iss流中依次读取各个字符串
        cout<<word<<endl;
}

问题:若将istringstream对象定义在循坏之外程序还能完成之前的功能吗?

string line, word;
istringstream iss;
while(getline(cin, line))
{
    iss.str(line); //调用str函数将流iss绑定到line(流对象iss中存有line的一个拷贝)
    while(iss>>word)
        cout<<word<<endl;
    //iss.clear();
}

不能。程序只会成功读取并处理文件中的第一行就终止了。

原因:当string流中的数据全部读出后,同样会触发“文件结束”信号,此时流iss不在有效了。

改正:可在某次使用完流之后调用clear函数使所有条件状态位复位,使其重新恢复有效状态。

使用ostringstream

例子:

ostringstream oss;
string word;
while(cin>>word)
    oss << word << " ";
cout<< oss.str() <<endl;

以上程序完成的事情是:依次读取所有输入的单词,并将单词“写入”到oss流对象,最后使用str函数将oss流对象包含的string以一行打印出来。(PS:最后也有一个空格)

原文地址:https://www.cnblogs.com/junjie_x/p/7653119.html