C++程序设计 湖南大学 杜四春、银红霞
第 14章 I/O流
14.1 C++流的概念
14.2 格式化 I/O
14.3 检测流操作的错误
14.4 文件流
14.5 字符串流
C++程序设计 湖南大学 杜四春、银红霞
C语言中没有提供专门的输入输出语句,同
样,C++语言中也没有专门的输入 /输出( I/O)
语句,C++中的 I/O操作是通过一组标准 I/O函
数和 I/O流来实现的。 C++的标准 I/O函数是从 C
语言继承而来的,同时对 C语言的标准 I/O函数
进行了扩充。 C++的 I/O流不仅拥有标准 I/O函
数的功能,而且比标准 I/O函数功能更强、更方
便、更可靠。
C++程序设计 湖南大学 杜四春、银红霞
14.1 C++流的概念
在 C++语言中, 数据的输入和输出 ( 简写为 I/O)
包括对标准输入设备键盘和标准输出设备显示器, 对
在外存磁盘上的文件和对内存中指定的字符串存储空
间进行输入输出这三个方面 。 对标准输入设备和标准
输出设备的输入输出简称为标准 I/O,对在外存磁盘上
文件的输入输出简称为文件 I/O,对内存中指定的字符
串存储空间的输入输出简称为串 I/O。
C++中把数据之间的传输操作称作流 。 在 C++中,
流既可以表示数据从内存传送到某个载体或设备中,
即输出流;也可以表示数据从某个载体或设备传送到
内存缓冲区变量中, 即输入流 。 在进行 I/O操作时, 首
先打开操作, 使流和文件发生联系, 建立联系后的文
件才允许数据流入或流出, 输入或输出结束后, 使用
关闭操作使文件与流断开联系 。
C++程序设计 湖南大学 杜四春、银红霞
C++为实现数据的输入和输出定义了一个庞大的类
库, 它包括的类主要有 ios,istream,ostream,iostream,
ifstream,ofstream,fstream,istrstream,ostrstream,
strstream等 。 其中 ios为根基类, 它直接派生四个类:
输入流类 istream,输出流类 ostream,文件流基类
fstreambase和字符串流基类 strstreambase。 输入文件流
类同时继承了输入流类和文件流基类 ( 当然对于根基
类是间接继承 ), 输出文件流类 ofstream同时继承了输
出流类和文件流基类, 输入字符串流类 istrstream同时
继承了输入流类和字符串流基类, 输出字符串流类
ostrstream同时继承了输出流类和字符串流基类, 输入
输出流类 iostream同时继承了输入流类和输出流类, 输
入输出文件流类 fstream同时继承了输入输出流类和文
件流基类, 输入输出字符串流类 strstream同时继承了输
入输出流类和字符串流基类 。
C++程序设计 湖南大学 杜四春、银红霞
C++ 系 统 中 的 I/O 类库, 其 所 有 类 被 包 含 在
iostream.h,fstream.h和 strstrea.h这三个系统头文件中,
各头文件包含的类如下:
iostream.h包含有,ios,iostream,istream,
ostream,iostream_withassign,istream_withassign,
ostream_withassign等 。
fstream.h包含有,fstream,ifstream,ofstream和
fstreambase,以及 iostream.h中的所有类 。
strstrea.h包含有,strstream,istrstream,ostrstream
和 strstreambase,以及 iostream.h中的所有类。
C++程序设计 湖南大学 杜四春、银红霞
在一个程序或一个编译单元(即一个程序文件)
中当需要进行标准 I/O操作时,则必须包含头文件
iostream.h,当需要进行文件 I/O操作时,则必须包含头
文件 fstream.h,同样,当需要进行串 I/O操作时,则必
须包含头文件 strstrea.h。在一个程序或编译单元中包含
一个头文件的命令格式为,#include<头文件名 >”,当
然若头文件是用户建立的,则头文件名的两侧不是使
用尖括号,而是使用双引号。当系统编译一个 C++文
件对 #include命令进行处理时,是把该命令中指定的文
件中的全部内容嵌入到该命令的位置,然后再编译整
个 C++文件生成相应的目标代码文件。
C++程序设计 湖南大学 杜四春、银红霞
C++不仅定义有现成的 I/O类库供用户使用,而且
还为用户进行标准 I/O操作定义了四个类对象,它们分
别是 cin,cout,cerr和 clog,其中 cin为
istream_withassign流类的对象,代表标准输入设备键盘,
也称为 cin流或标准输入流,后三个为
ostream_withassign流类的对象,cout代表标准输出设备
显示器,也称为 cout流或标准输出流,cerr和 clog含义
相同,均代表错误信息输出设备显示器。因此当进行
键盘输入时使用 cin流,当进行显示器输出时使用 cout
流,当进行错误信息输出时使用 cerr或 clog。
C++程序设计 湖南大学 杜四春、银红霞
C++的流通过重载运算符, <<”和, >>”执行输入
和输出操作 。 输出操作是向流中插入一个字符序列,
因此, 在流操作中, 将运算符, <<”称为插入运算符 。
输出操作是从流中提取一个字符序列, 因此, 将运算
符, >>”称为提取运算符 。
1,cout
在 ostream输出流类中定义有对左移操作符 <<重载
的一组公用成员函数, 函数的具体声明格式为:
ostream& operator<<(简单类型标识符);
C++程序设计 湖南大学 杜四春、银红霞
简单类型标识符除了与在 istream流类中声明右移
操作符重载函数给出的所有简单类型标识符相同以外,
还增加一个 void* 类型,用于输出任何指针(但不能是
字符指针,因为它将被作为字符串处理,即输出所指
向存储空间中保存的一个字符串)的值。由于左移操
作符重载用于向流中输出表达式的值,所以又称为插
入操作符。如当输出流是 cout时,则就把表达式的值
插入到显示器上,即输出到显示器显示出来。
C++程序设计 湖南大学 杜四春、银红霞
2,cin
在 istream输入流类中定义有对右移操作符 >>重载
的一组公用成员函数, 函数的具体声明格式为:
istream& operator>>( 简单类型标识符 &) ;
简单类型标识符可以为 char,signed char,
unsigned char,short,unsigned short,int,unsigned
int,long,unsigned long,float,double,long
double,char*,signed char*,unsigned char*之中的
任何一种,对于每一种类型都对应着一个右移操作符
重载函数。由于右移操作符重载用于给变量输入数据
的操作,所以又称为提取操作符,即从流中提取出数
据赋给变量。
C++程序设计 湖南大学 杜四春、银红霞
3,cerr
cerr类似标准错误文件 。 cerr与 cout的差别在于:
( 1) cerr是不能重定向的;
( 2) cerr不能被缓冲, 它的输出总是直接传达到
标准输出设备上 。
错误信息是写到 cerr的项 。 即使在各种其他输出语
句中, 如果使用下列语句, 则错误信息, Error”总能保
证在显示器上显示出来:
cerr <<,Error” <<,\n”;
C++程序设计 湖南大学 杜四春、银红霞
4,clog
clog是不能重定向的, 但是可以被缓冲 。 在某些系
统中, 由于缓冲, 使用 clog代替 cerr可以改进显示速度:
clog <<,Error” <<,\n”;
C++程序设计 湖南大学 杜四春、银红霞
14.2 格式化 I/O
14.2.1 ios类中的枚举常量
在根基类 ios中定义有三个用户需要使用的枚举类型,
由于它们是在公用成员部分定义的, 所以其中的每个枚举
类型常量在加上 ios::前缀后都可以为本类成员函数和所有外
部函数访问 。 在三个枚举类型中有一个无名枚举类型, 其
中定义的每个枚举常量都是用于设置控制输入输出格式的
标志使用的 。 该枚举类型定义如下:
enum {skipws,left,right,internal,dec,oct,hex,showbase,showpoint,
uppercase,showpos,scientific,fixed,unitbuf,stdio
};
C++程序设计 湖南大学 杜四春、银红霞
各枚举常量的含义如下:
( 1) skipws
利用它设置对应标志后, 从流中输入数据时跳过
当前位置及后面的所有连续的空白字符, 从第一个非
空白字符起读数, 否则不跳过空白字符 。 空格, 制表
符 ‘ \t’,回车符 ‘ \r’和换行符 ‘ \n’统称为空白符 。 默
认为设置 。
( 2) left,right,internal
left在指定的域宽内按左对齐输出,right按右对齐
输出,而 internal使数值的符号按左对齐、数值本身按
右对齐输出。域宽内剩余的字符位置用填充符填充。
默认为 right设置。在任一时刻只有一种有效。
C++程序设计 湖南大学 杜四春、银红霞
( 3) dec,oct,hex
设置 dec对应标志后, 使以后的数值按十进制输
出, 设置 oct后按八进制输出, 而设置 hex后则按十六进
制输出 。 默认为 dec设置 。
( 4) showbase
设置对应标志后使数值输出的前面加上, 基指示
符,, 八进制数的基指示符为数字 0,十六进制数的基
指示符为 0x,十进制数没有基指示符 。 默认为不设置,
即在数值输出的前面不加基指示符 。
( 5) showpoint
强制输出的浮点数中带有小数点和小数尾部的无
效数字 0。默认为不设置。
C++程序设计 湖南大学 杜四春、银红霞
( 6) uppercase
使输出的十六进制数和浮点数中使用的字母为大
写 。 默认为不设置 。 即输出的十六进制数和浮点数中
使用的字母为小写 。
( 7) showpos
使输出的正数前带有正号, +”。 默认为不设置 。
即输出的正数前不带任何符号 。
( 8) scientific,fixed
进行 scientific设置后使浮点数按科学表示法输出,
进行 fixed设置后使浮点数按定点表示法输出。只能任
设其一。缺省时由系统根据输出的数值选用合适的表
示输出。
C++程序设计 湖南大学 杜四春、银红霞
( 9) unitbuf,stdio
这两个常量很少使用, 所以不予介绍 。
在 ios中定义的第二个枚举类型为:
enum open_mode {in,out,ate,app,trunc,nocreate,
noreplace,binany};
其中的每个枚举常量规定一种文件打开的方式,
在定义文件流对象和打开文件时使用 。
在 ios中定义的第三个枚举类型为:
enum seek_dir {beg,cur,end};
其中的每个枚举常量用于对文件指针的定位操作
上。
C++程序设计 湖南大学 杜四春、银红霞
14.2.2 ios类中的成员函数
ios类提供成员函数对流的状态进行检测和进行输入输
出格式控制等操作, 每个成员函数的声明格式如下:
int bad( ) ; int eo( ) ;
int fail( ) ; void clear( ) ;
char fill( ) ; char fill( char c) ;
long flags( ) ; long flags( long f) ;
int good( ) ; int precision( ) ;
int rdstate( ) ; int precision( int n) ;
int width( ) ; long setf( long f) ;
int width( int w) ; long unsetf( long f) ;
C++程序设计 湖南大学 杜四春、银红霞
因为所有 I/O流类都是 ios的派生类,所以它们的对
象都可以调用 ios类中的成员函数和使用 ios类中的格式
化常量进行输入输出格式控制。下面以标准输出流对
象 cout为例说明输出的格式化控制。
例 14-1
C++程序设计 湖南大学 杜四春、银红霞
14.2.3 格式控制操作符
数据输入输出的格式控制还有更简便的形式,就
是使用系统头文件 iomanip.h中提供的操纵符。使用这
些操纵符不需要调用成员函数,只要把它们作为插入
操作符 <<(个别作为提取操作符 >>)的输出对象即可。
这些操纵符及功能如下:
dec oct hex ws
endl ends flush
setiosflags( long f) resetiosflags( long f)
setfill( int c) setprecision( int n)
setw( int w)
C++程序设计 湖南大学 杜四春、银红霞
在上面的操纵符中,dec,oce,hex,endl,ends,
flush和 ws除了在 iomanip.h中有定义外,在 iostream.h中
也有定义。所以当程序或编译单元中只需要使用这些
不带参数的操纵符时,可以只包含 iostream.h文件,而
不需要包含 iomanip.h文件。
下面以标准输出流对象 cout为例, 说明使用操作符
进行的输出格式化控制 。
例 14-4:
C++程序设计 湖南大学 杜四春、银红霞
14.3 检测流操作的错误
在 I/O流的操作过程中可能出现各种错误, 每一个
流都有一个状态标志字, 以指示是否发生了错误以及
出现了哪种类型的错误, 这种处理技术与格式控制标
志字是相同的 。 ios类定义了以下枚举类型:
enum io_state
{
goodbit =0x00,//不设置任何位, 一切正常
eofbit =0x01,//输入流已经结束, 无字符可读入
failbit =0x02,//上次读 /写操作失败, 但流仍可使用
badbit =0x04,//试图作无效的读 /写操作, 流不再可用
hardfail=0x80 //不可恢复的严重错误
};
C++程序设计 湖南大学 杜四春、银红霞
对应于这个标志字各状态位, ios类还提供了以下
成员函数来检测或设置流的状态:
int rdstate(); //返回流的当前状态标志字
int eof(); //返回非 0值表示到达文件尾
int fail(); //返回非 0值表示操作失败
int bad(); //返回非 0值表示出现错误
int good(); //返回非 0值表示流操作正常
int clear(int flag=0); //将流的状态设置为 flag
为提高程序的可靠性,应在程序中检测 I/O流的操
作是否正常。当检测到流操作出现错误时,可以通过
异常处理来解决问题。
C++程序设计 湖南大学 杜四春、银红霞
14.4 文件流
14.4.1 文件的概念
在磁盘上保存的信息是按文件的形式组织的,每
个文件都对应一个文件名,并且属于某个物理盘或逻
辑盘的目录层次结构中一个确定的目录之下。一个文
件名由文件主名和扩展名两部分组成,它们之间用圆
点(即小数点)分开,扩展名可以省略,当省略时也
要省略掉前面的圆点。文件主名是由用户命名的一个
有效的 C++标识符,为了同其他软件系统兼容,一般
让文件主名为不超过 8个有效字符的标识符,同时为了
便于记忆和使用,最好使文件主名的含义与所存的文
件内容相一致。
C++程序设计 湖南大学 杜四春、银红霞
文件扩展名也是由用户命名的,1至 3个字符组成
的、有效的 C++标识符,通常用它来区分文件的类型。
如在 C++系统中,用扩展名 h表示头文件,用扩展名 cpp
表示程序文件,用 obj表示程序文件被编译后生成的目
标文件,用 exe表示连接整个程序中所有目标文件后生
成的可执行文件。对于用户建立的用于保存数据的文
件,通常用 dat表示扩展名,若它是由字符构成的文本
文件则也用 txt作为扩展名,若它是由字节构成的、能
够进行随机存取的内部格式文件则可用 ran表示扩展名。
C++程序设计 湖南大学 杜四春、银红霞
在 C++程序中使用的保存数据的文件按存储格式分
为两种类型,一种为字符格式文件,简称字符文件,
另一种为内部格式文件,简称字节文件。字符文件又
称 ASCII码文件或文本文件,字节文件又称二进制文件。
在字符文件中,每个字节单元的内容为字符的 ASCII码,
被读出后能够直接送到显示器或打印机上显示或打印
出对应的字符,供人们直接阅读。在字节文件中,文
件内容是数据的内部表示,是从内存中直接复制过来
的。当然对于字符信息,数据的内部表示就是 ASCII码
表示,所以在字符文件和在字节文件中保存的字符信
息没有差别,但对于数值信息,数据的内部表示和
ASCII码表示截然不同,所以在字符文件和在字节文件
中保存的数值信息也截然不同。
C++程序设计 湖南大学 杜四春、银红霞
要在程序中使用文件时,首先要在开始包含
#include<fstream.h>命令。由它提供的输入文件流类
ifstream、输出文件流类 ofstream和输入输出文件流类
fstream定义用户所需要的文件流对象,然后利用该对
象调用相应类中的 open成员函数,按照一定的打开方
式打开一个文件。文件被打开后,就可以通过流对象
访问它了,访问结束后再通过流对象关闭它。
C++程序设计 湖南大学 杜四春、银红霞
14.4.2 文件的打开与关闭
流可以分为 3类:输入流, 输出流以及输入 /输出
流, 相应地必须将流说明为 ifstream,ofstream以及
fstream类的对象 。 例如:
ifstream ifile; //说明一个输入流
ofstream ofile; //说明一个输出流
fstream iofile; //说明一个输入 /输出流
说明了流对象之后, 可使用函数 open( ) 打开文
件 。 文件的打开即是在流与文件之间建立一个连接 。
open( ) 的函数原型为:
void open(const char * filename,int mode,int prot=filebuf::openprot);
C++程序设计 湖南大学 杜四春、银红霞
其中, filename是文件名字, 它可包含路径说明 。
mode说明文件打开的模式, 它对文件的操作影响
重大, mode的取值必须是以下值之一:
ios::in 打开文件进行读操作
ios::out 打开文件进行写操作
ios::ate 打开时文件指针定位到文件尾
ios::app 添加模式, 所有增加都在文件尾部
进行
ios::trunc 如果文件已存在则清空原文件
ios::nocreate 如果文件不存在则打开失败
ios::noreplace 如果文件存在则打开失败
ios::binary 二进制文件(非文本文件)
C++程序设计 湖南大学 杜四春、银红霞
对于 ifstream流, mode的默认值为 ios::in;对于
ofstream流, mode的默认值为 ios::out。
prot决定文件的访问方式, 取值为:
0 普通文件
1 只读文件
2 隐含文件
4 系统文件
一般情况下,该访问方式使用默认值。
与其他状态标志一样,mode的符号常量可以用位
或运算,|”组合在一起,如 ios::in|ios::binary表示以只读
方式打开二进制文件。
C++程序设计 湖南大学 杜四春、银红霞
注意:打开文件操作并不能保证总是正确的, 如
文件不存在, 磁盘损坏等原因可能造成打开文件失败 。
如果打开文件失败后, 程序还继续执行文件的读 /写操
作, 将会产生严重错误 。 在这种情况下, 应使用异常
处理以提高程序的可靠性 。
如果使用构造函数或 open()打开文件失败,流
状态标志字中的 failbit,badbit或 hardbit将被置为 1,并
且在 ios类中重载的运算符“!”将返回非 0值。通常可
以利用这一点检测文件打开操作是否成功,如果不成
功则作特殊处理。
C++程序设计 湖南大学 杜四春、银红霞
每个文件流类中都提供有一个关闭文件的成员函
数 close( ), 当打开的文件操作结束后, 就需要关闭
它, 使文件流与对应的物理文件断开联系, 并能够保
证最后输出到文件缓冲区中的内容, 无论是否已满,
都将立即写入到对应的物理文件中 。 文件流对应的文
件被关闭后, 还可以利用该文件流调用 open成员函数
打开其他的文件 。
关闭任何一个流对象所对应的文件, 就是用这个
流对象调用 close( ) 成员函数即可 。 如要关闭 fout流所
对应的 a:\xxk.dat文件, 则关闭语句为:
fout.close();
C++程序设计 湖南大学 杜四春、银红霞
14.4.3 文件的读写
1,文件读写方法
( 1) 使用流运算符直接读写 。
文件的读 /写操作可以直接使用流的插入运算符
,<<”和提取运算符, >>”,这些运算符将完成文件的
字符转换工作 。
( 2) 使用流成员函数
常用的输出流成员函数为,put函数, write函数
常用的输入流成员函数如下,get函数, getline函
数, read函数
C++程序设计 湖南大学 杜四春、银红霞
2,文本文件的读写
文本文件只适用于那些解释为 ASCII码的文件。处
理文本文件时将自动作一些字符转换,如输出换行字
符 0x0A时将转换为回车 0x0D与换行 0x0A两个字符存入
文本文件,读入时也会将回车与换行两个字符合并为
一个换行字符,这样内存中的字符与写入文件中的字
符之间就不再是一一对应关系。文本文件的结束以
ASCII码的控制字符 0x1A表示。
C++程序设计 湖南大学 杜四春、银红霞
3,二进制文件的读写
二进制文件不同于文本文件, 它可用于任何类型
的文件 ( 包括文本文件 ), 读写二进制文件的字符不
作任何转换, 读写的字符与文件之间是完全一致的 。
一般地,对二进制文件的读写可采用两种方法:
一种是使用 get()和 put();另一种是使用 read()
和 write()。
C++程序设计 湖南大学 杜四春、银红霞
4,文件的随机读写
( 1) 输出流随机访问函数 。
输出流随机访问函数有 seekp和 tellp。
( 2) 输入流随机访问函数 。
输入流随机访问函数有 seekg和 tellg。
C++程序设计 湖南大学 杜四春、银红霞
14.5 字符串流
字符串流类包括输入字符串流类 istrstream,输出
字符串流类 ostrstream和输入输出字符串流类 strstream
三种。它们都被定义在系统头文件 strstrea.h中。只要在
程序中带有该头文件,就可以使用任一种字符串流类
定义字符串流对象。每个字符串流对象简称为字符串
流。
三种字符串流类的构造函数声明格式分别如下:
istrstream(const char* buffer);
ostrstream(char* buffer,int n);
strstream(char* buffer,int n,int mode);
对字符串流的操作方法通常与对字符文件流的操
作方法相同。