C++ Primer- 流的条件状态以及缓冲区管理
IO标准库提供了一系列条件状态成员,用来标记IO对象是否处于可用状态。使用strm::iostate类型的值来表示条件状态,
这是一个跟机器有关的整型值,通过判断特定的一些位是否为1来判断流处于什么状态,有三个常量strm::badbit, strm::failbit, strm::eofbit 分别代表流被破坏,失败的IO操作和流已经到达文件尾。那么我们如何判断一个流比如说s处于什么状态呢,s.eof()若为true,表示到达文件尾,s.bad()若为ture,代表流被破坏,而 s.fail()若为true,则代表s遇到了失败的IO操作。
我们现在可以通过这三个函数来判断流的状态,那么下一步,该如何改变这个流的状态呢,有以下几个函数:
s.clear() 清除所有不可用的状态,重新使流有效
s.clear(flag) 设置流的某一个不可用状态,如badbit状态,所以flag是strm::iostate类型的
s.setstate(flag) 添加流的某一个不可用状态,如failbit状态,同样是strm::iostate类型值
s.rdstate() 返回流的当前状态,是strm::iostate类型值
这里我们通过一个例子来练习使用流的条件状态,题目是C++ Primer 练习8.3原题:
编写一个函数,唯一形参和返回值都是istream&类型,该函数一直读取流直到到达文件结束符,同时把读到的内容输出到标准输出中,最后,重设流使其有效并返回,通过cin为实参实现调用来测试该函数。
代码和注释如下:
/**
* @file main.cpp
* @brief 编写一个函数,可以一直读取流直到文件结束 并输出
* @details
* @author jason.mrbourne@gmail.com
* @date 2014-5-20
*/
#include <iostream>
#include <stdexcept>
using namespace std;
istream& get(istream& in)
{
int ival;
//遇到文件结束符前一直读入数据
while (in >> ival, !in.eof())
{
if (in.bad()) //出现系统级故障
throw runtime_error("IO stream corrupted");
if (in.fail()) //出现可恢复错误
{
cerr << "bad data, try again" << endl; //提示用户
//有一些编译器不支持in.clear(istream::failbit)语句 所以这里用in.clear()
in.clear(); //恢复流
//跳过200个字符或者遇到空格或EOF为止 因此输入时必须以空格为间隔
in.ignore(200, ' ');
continue; //继续读入数据
}
//读入正常
cout << ival << '\t';
}
in.clear();
return in;
}
//在主函数中测试
int main()
{
int ival;
get(cin); //这里用cin作为我们要读取的流
cin >> ival; //重新使用恢复后的流
cout << ival << endl;
return 0;
}
输出缓冲区的管理,输出缓冲区中存放着将要输出的数据,当程序结束或缓冲区满或执行一些清空缓冲区的操作时,系统会刷新缓冲区。
c++提供3个操作符用于刷新流,用法如下:
cout << “hi” << flush; //刷新缓冲区,不添加数据
cout << “hi” << ends; //刷新缓冲区,并在输出中插入一个null空字符
cout << “hi” << endl; //刷新缓冲区,并在输出中插入一个换行符
也可以用unitbuf操纵符来实现刷新,使用unitbuf会在每一次输出后刷新缓冲区。
cout << unitbuf << “first” << “second”<< nounitbuf;
这样在输出first后缓冲区被刷新,输出second后,缓冲区再次被刷新,此时缓冲区里啥也没有。但要最后用nounitbuff把流恢复正常。
由于程序崩溃时,系统不会自动刷新缓冲区,那么此时想通过输出来找出错误发生的原因就变得异常艰难,基于这个原因,输出时应当尽可能使用endl而非’\n’。
我们还可以将输入和输出绑在一起,默认标准库是将cout与cin绑在一起,这样在读cin的时候,会导致cout的缓冲区被刷新,我们可以使用tie()来制定需要绑定的流,例如:
cin.tie(&cout); //tie的参数为指向ostream对象的指针
ostream* old_tie = cin.tie(); //返回当前与cin绑定的流指针
cin.tie(0); //解除cin的所有绑定,这样在读cin时,cout缓冲区将不会刷新
cin.tie(&cerr); //将cin与cerr绑定
cin.tie(0); //解除cin与cerr的绑定
cin.tie(old_tie); //恢复cin与cout的绑定