在写代码的过程中,我们最常做的事就是io操作,无论是对控制台,还是文件。但一段时间不写代码就忘了,这里理一下C++标准I/O库的具体类和操作。
C++的标准I/O库包括我们经常使用的iostream,fstream,以及不太经常使用的stringstream。前两者是对控制台和文件的I/O操作,stringstream则可以使用I/O操作对内存中的数据进行格式化操作。C++的标准I/O操作相对与C来说,更加的简明,安全,但执行效率会有所下降。
标准I/O库类继承体系
对于编程语言来说,在概念上,从控制终端、磁盘文件以及内存中读取数据都应该不影响I/O操作,C++为了解决支持不同设备的字符流,通过面向对象的思想(废话了,你要不用这个思想,你还是什么C++),通过继承来实现一组类,分别处理控制终端、磁盘文件,以及内存数据的I/O操作,下图是引用cplusplus官网关于输入输出流类继承体系的关系图,自己画了一下,如下:

由上图可以知道,I/O操作的基类是ios_base,各个类的用途如下:
vc3RyZWFtPiAgICAgICAgICAgICAgICAgICAgIGlzdHJlYW0gICAgtNPB99bQtsHIocr9vt0gICAgICAgICAgICAgICAgICAgICBvc3RyZWFtICAgz/LB99bQ0LTK/b7dICAgICAgICAgICAgICAgICAgICAgaW9zdHJlYW0gILbUwfe9+NDQtsHQtLLZ1/ejrMXJyfrT2mlzdHJlYW26zW9zdHJlYW08YnI+Cjxmc3RyZWFtPiAgICAgICAgICAgICAgICAgICAgIGlmc3RyZWFtICAgILTTzsS8/tbQtsHIocr9vt2jrMXJyfrT2mlzdHJlYW0gICAgICAgICAgICAgICAgICAgICBvZnN0cmVhbSAgIM/yzsS8/tbQ0LTK/b7do6zFycn609pvc3RyZWFtICAgICAgICAgICAgICAgICAgICAgZnN0cmVhbSAgICAgtsHQtM7EvP6jrCDFycn609ppb3N0cmVhbTxzc3RyZWFtPiAgICAgICAgICAgICAgICAgICAgIGlzdHJpbmdzdHJlYW0gICAgtsHIoXN0cmluZ7bUz/OjrMXJyfrT2mlzdHJlYW0gICAgICAgICAgICAgICAgICAgICBvc3RyaW5nc3RyZWFtICAg0LRzdHJpbme21M/zo6zFycn609pvc3RyZWFtICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc3RyZWFtICAgICC2wdC0c3RyaW5nttTP86OsxcnJ+tPaaW9zdHJlYW08YnI+Cgo8cD5DJiM0MzsmIzQzO7Hq17y21NPaSS9PzOXPtaOstqjS5cHLu/mxvrXEwfcmIzI2Njg0O8q9serWvihoZXgsIGRlYyy1yCmjrM7EvP608r+qxKPKvShpbiwgb3V0LCBiaW61yCmjrMH3tcTXtMysserWvihmYWlsYml0tcgpo6zS1Lywz+C52LXEuq/K/bXIo6zI58/C1NpsaW51eCDPwi91c3IvaW5jbHVkZS9jJiM0MzsmIzQzOy80LjYvYml0cy9pb3NfYmFzZS5o1tC52NPa1eLQqbHq1r61xMO2vtm2qNLlo7o8L3A+CgrB9yYjMjY2ODQ7yr2x6ta+PGJyPgoKPHA+PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;">enum _Ios_Fmtflags { _S_boolalpha = 1L << 0, _S_dec = 1L << 1, _S_fixed = 1L << 2, _S_hex = 1L << 3, _S_internal = 1L << 4, _S_left = 1L << 5, _S_oct = 1L << 6, _S_right = 1L << 7, _S_scientific = 1L << 8, _S_showbase = 1L << 9, _S_showpoint = 1L << 10, _S_showpos = 1L << 11, _S_skipws = 1L << 12, _S_unitbuf = 1L << 13, _S_uppercase = 1L << 14, _S_adjustfield = _S_left | _S_right | _S_internal, _S_basefield = _S_dec | _S_oct | _S_hex, _S_floatfield = _S_scientific | _S_fixed, _S_ios_fmtflags_end = 1L << 16 };
文件打开模式
enum _Ios_Openmode
{
_S_app = 1L << 0,
_S_ate = 1L << 1,
_S_bin = 1L << 2,
_S_in = 1L << 3,
_S_out = 1L << 4,
_S_trunc = 1L << 5,
_S_ios_openmode_end = 1L << 16
};
流的状态标志
enum _Ios_Iostate
{
_S_goodbit = 0,
_S_badbit = 1L << 0,
_S_eofbit = 1L << 1,
_S_failbit = 1L << 2,
_S_ios_iostate_end = 1L << 16
};
I/O流的状态
为了更好地管理I/O操作,标准定义了一系列条件状态标志和函数,用来管理流对象,比如说是否可用,以及出现的错误。对于流的状态标志是在ios_base类中进行定义的,所以流的状态标志管理适用于终端、文件、string流对象。流的状态定义为:ios_base::iostate,具体有哪些值在ios_base中有定义,如下:
typedef _Ios_Iostate iostate;
static const iostate badbit = _S_badbit; //流被破坏 (流本身的问题)
static const iostate eofbit = _S_eofbit; //流已到结尾
static const iostate failbit = _S_failbit; //I/O失败(操作本身上的逻辑问题)
static const iostate goodbit = _S_goodbit; //好流...正常
为了管理上述的状态标志,标准在ios类中提供了以下函数来进行处理:
iostate rdstate() const //返回流的条件状态
void clear(iostate __state = goodbit); //设置流的条件状态,默认设置为正常
void setstate(iostate __state); //设置流的状态,和clear的区别:不会清除其他标志
bool good() const //流的是否正常
bool eof() const //流是否到结尾
bool fail() const //流是否I/O失败
bool bad() const //流是否被破坏
可以通过下面代码简单进行状态标志的测试:
#include
using std::cout;
using std::cin;
using std::endl;
int main()
{
int a;
cin>>a;
cout<<"goodbit: "<
>a; cout<<"goodbit: "<
输入1 a后显示如下:
1
goodbit: 1 eofbit: 0 failbit: 0 badbit: 0
a
goodbit: 0 eofbit: 0 failbit: 1 badbit: 0
由上可知非法输入会导致I/O失败,failbit流标志生效。
文件I/O操作
C++提供了ifstream和ofstream类负责文件I/O的读和写操作,类fstream负责文件的读写,继承关系文章开头已说明。前面说了,在ios_base基类中定义了文件打开模式,
typedef _Ios_Openmode openmode;
/// Seek to end before each write.
static const openmode app = _S_app;
/// Open and seek to end immediately after opening.
static const openmode ate = _S_ate;
/// Perform input and output in binary mode (as opposed to text mode).
static const openmo