输入输出(IO)是指计算机同任何外部设备之间的数据传递。常见的输入输出设备有文件、键盘、打印机、屏幕等。数据可以按记录(或称数据块)的方式传递,也可以 流的方式传递。
所谓记录,是指有着内部结构的数据块。记录内部除了有需要处理的实际数据之外,还可能包含附加信息,这些附加信息通常是对本记录数据的描述。
流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出。
C++IO流,特指以流的方式进行输入输出的ISO/ANSI标准C++库的输入输出类库,也就是专门负责处理IO操作的一套系统。任何需要传递的数据,都要经过这套系统的处理。
IO操作的过程中,任何需要被传递的数据,在经过IO类库处理前后是不同的。这样,我们可以把数据的表示分为两种:内部表示和外部表示。
数据的内部表示便于程序进行数据处理。典型的内部表示有:整型数的二进制表示、浮点数的IEEE表示、字符的ASCII或Unicode编码表示。数据的外部表示则根据不同的外部设备的需要,有具体不同的表现形式。如果外部数据表示是可读的字符序列,则称为文本IO,否则为二进制IO。标准IO流的主要目的是支持文本IO,不直接支持二进制IO。
虽然IO流是以流的方式进行数据传递,但这并不表明传递的数据不能有任何结构,而是指IO流的概念是以流的方式进行输入输出,所传递数据的内部结构隐藏在对流数据的解释中。
在IO流里,输入输出分为4步:格式化/解析,缓冲,编码转换和传递。
格式化/解析:在内部数据表示(以字节为单位)与外部数据表示(以字符为单位)之间进行双向转换。例如一个2字节的整数10002,就需要5个字符来表示。
缓冲:用于在格式/解析与传递只加缓存字符序列。对于输出,较短的字符序列格式化之后并不马上输出,而是保存在缓冲区里,待累积到一定规模之后再传递到外部设备。相反,从外部设备读入的大量数据也是先放在缓冲区,然后逐步取出完成输入。默认时,IO流的输入输出都是经过缓冲的,也可以让IO流工作在无缓冲模式下。
编码转换: 是将一种字符表达式转换成另一种字符表达式。如果格式化产生的字符表达式与外部字符表达式不同(输出时),或者外部表达式与IO流能解析的表达式不同(输入时),就必须进行编码转换。如多字节编码与宽字符编码之间的转换等。多数情况下并不需要进行编码转换。
传递:主要是与外部设备进行通信。输出时,传递负责将经过格式化、缓冲即编码转换后的字符序列发送到外部设备;输入时,则负责将外部设备抽取数据,为其后进行的编码转换、缓冲及解析提供字符序列。
IO流类库在不同平台的具体实现上,可能会有所变化,但从总体设计上来看,C++流库主要由两个流类层次组成:
(1)以streambuf类为父类的类层次
主要完成信息通过缓冲区的交换。派生层次如下:
缓冲区:是一个队列数据结构,由一字符序列和两个指针组成,这两个指针分别指向字符要被插入或被取出的位置。
streambuf类为所有的streambuf类层次对象设置了一个固定的内存缓冲区,动态划分为两部分:
用做输入的取区,用取指针指示当前取字符位置。
用做输出的存区,用存指针指示当前存字符位置。
(2)以ios类为父类的类层次
ios类及其派生类是在streambuf类实现的通过缓冲区的信息交换的基础上,进一步增加了各种格式化的输入/输出控制方法。它们为用户提供使用流类的接口,它们均有一个指向streambuf的指针。
ios类有四个直接派生类:
istream
ostream
fstreambase
strstreambase
这四种流作为流库中的基本流类。ios类的派生层次如下:
C++语言开发了自己的IO流类库,用以取代C语言的基本输入输出函数族。对于有经验的C程序员来说,C语言提供的IO函数库时有效且方便的。但是,C语言的IO函数库有其自身的缺点,特别是在C++这种面向对象的程序设计语言中,C语言函数库无法直接支持面向对象的程序设计。因此,C++语言开发自己的IO流类库是必然的。具体来说,IO流类库具有以下优点。
(1)简明与可读性
IO流类库用IO运算符(提取运算符>>和插入运算符<<)代替了不同的输入输出函数名,如printf和scanf等。从直观来看,这种改变使得IO语句更为简明。另外,也减轻了程序员在记忆函数名和书写程序上的一些负担。例如:
printf(“n=%d,a=%f\n”,n,a);
cout<<”n=”<<n<<”,a=”<<a<<endl;
虽然两条语句的输出结果是一样的,但是后者更加简明,直观,易写,易读。
(2)类型安全(type safe)
所谓类型安全,是指编译器所理解的数据实体(如变量。指针所指向的数据等)的类型,与实际数据实体的实际类型或对该数据所进行的操作之间保持一致性。在进行IO操作时,编译器将自动检查实参的表达式类型来调用IO流类相应的重载版本的成员函数,来完成输入输出。而采用C的IO函数,必须显示指明操作的数据类型,如采用printf()函数,由于其参数中的数据类型必须由程序员以参数格式%d,%f,%c,%s,容易出错。
(3)易于扩充
C++语言的IO流类库,是建立在类的继承关系、模板和操作符重载等机制的基础上的。把原来C语言中的左、右移位运算符<<和>>,通过运算符重载的方法,定 义为插入(输出)和提取(输入)运算符。这就为输入输出功能对于各种用户定义的类型数据的扩充,创造了方便的条件。
用户可以采用输入输出操作符的重载来完成用户想要的输入输出功能。例如,用于复数类Complex的输出操作符重载函数可以定义为:
friend ostream& operator<<(ostream& s,const Complex& c){
s<<c.real<<"+"<<c.image<<"i"<<endl;
return s;
}
输入输出操作符有个固定的格式,以上是一种常用的格式。由于C语言并不支持函数重载,也不直接支持面向对象的程序设计,所以想扩充C语言的输入输出函数使它们支持用户定义的新数据类型,是一件非常困难的事情。
[1]陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008[P323-P326].
[2]http://wenku.baidu.com/view/61ed0dc2d5bbfd0a79567328.html?re=view