概 述
在C语言中,输入/输出系统的特点是缺乏类型检查机制。如printf函数,在格式控制字符串后的参数,即使类型和个数与其不匹配,编译是不会出错,但运行时会得到错误的结果。C++提供了新的输入/输出方式。其主要目标是建立一个类型安全,扩展性好的输入/输出系统。在一个类型安全的输入/输出系统中,类似上述printf的错误在编译时就可发现。一个理想的可扩展的输入/输出系统必须能以两种方式进行扩展:
1.能够包含用户定义的数据类型。
2.能够包含新的输入/输出方法。
在C++中输入/输出流库充分利用了C++的面向对象的特性实现了上述目标。
流的概念
所谓流是指数据从一个位置流向另一个位置。流是C++为输入/输出提供的一组类,都放在流库中。流总是与某一设备相联系(例如,键盘,屏幕或硬盘等),通过使用流类中定义的方法,就可以完成对这些设备的输入/输出操作。一般,若要在流中存储数据,这个流为输出流;要从流中读取数据,这个流为输入流。有的流既是输入流,又是输出流。流类形成的层次结构就构成流类库,即流库。与C 语言中的输入/输出流库一样,C++的输入输出流库不是语言的一部分,而是作为一个独立的函数库提供的。因此,在使用时需要包含相应的头文件。
输入流和输出流:在编写程序时,常要输入一些数据,在处理完数据之后,有要把结果输出. c++ 没有专门的输入输出语句,输入输出都有流库来处理.通过输出流,拥护可以从这些设备中读取数据;通过输出流则可以往设备中写数据.
输出流:我们用cout输出过数据.实质上.cout 就是输出流类ostream的派生类预定义的一个对象.它与标准输出设备相联系,以便把数据送往屏幕显示.在ostream类中,重载了<<运算符,用来处理各种内部类型的输出
输入流:c++也为输入定义了一个流类istream.这个类中重载了>>运算符,以便从先观的设备中读取数据,对应与插入运算.这里>>运算符内称为析取运算.
重载插入运算符
在ostream类中,重载了<<运算符,用来处理各种内部类型的输出,ostream的定义放在iostream.h头文件中:
class ostream:public virtual ios{
//……
public:
ostream&operator<<(const char*);
ostream&operator<<(char);
ostream&operator<<(short i){
return *this<<int(i);
}
ostream&operator<<(int);
ostream&operator<<(long);
ostream&operator<<(double);
ostream&operator<<(const void*);
// ……
};
注:其中char*用于输出字符串,void*用于输出指针的地址值
重载析取运算符
class istream:public virtual ios{
//……
public:
istream&operator>>(char *);//字符串
istream&operator>>(char &);//字符
istream&operator>>(shirt &);
istream&operator>>(int &);
istream&operator>>(long &);
istream&operator>>(float &);
istream&operator>>(double &);
//...
};
istream将ios作为基类.他也为所有内部类型定义了析取运算.在iostream.h头文件总预定义了一个istream派生类的对象cin,cin与标准输入相联系,用语从键盘输入数据.
例如:
//...
int i;
cin >>i;
//...
cin>>i将调用cin.operator.(i),把键盘中输入的整数存放到变量i中.
格式控制
在前面,输入/输出的数据没有指定格式,它们都按缺省的格式输入/输出。然而,有时需要对数据格式进行控制。这时需利用ios类中定义的格式控制成员函数,通过调用它们来完成格式的设置。ios类的格式控制函数如下所示:
1.宽度控制
ios的成员函数width()是指定在输入/输出一个数字或串时,缓冲去可存储的最大字符数,在输入流总,长用他来防止缓冲区溢出,
例如:
char buffer[20];
cin.width(20);
cin>>buffer;
width()调用告诉cin插入操作一次最多能读入20个字符,从而可以保证buffer中的数不会因为超过20而溢出.
2.格式状态
在ios类中,定义了一个表示流状态的枚举,枚举中有各种标志,用它们进一步控制输入输出,
在ios类中定义了几个成员函数,用来设置,读取和取消标志位:
long flags( ) const 返回当前的格式标志。
long flays(long newflag) 设置格式标志为newflag,返回旧的格式标志。
long setf(long bits) 设置指定的格式标志位,返回旧的格式标志。
long setf(long bits,long field)将field指定的格式标志位置为bits,返回旧的格式标志
long unsetf(long bits) 清除bits指定的格式标志位,返回旧的格式标志。
long fill(char c) 设置填充字符,缺省条件下是空格。
char fill( ) 返回当前填充字符。
int precision(int val) 设置精确度为val,控制输出浮点数的有效位,返回旧值。
int precision( ) 返回旧的精确度值。
int width(int val) 设置显示数据的宽度(域宽),返回旧的域宽。
int width( )只返回当前域宽,缺省宽度为0。这时插入操作能按表示数据的最小宽度显示数据
3.控制符
在前面.控制输入/输出采用的是函数的形式,但他们使用起来并不方便.为此,c++提供了控制符,他可以直接插入或析取运算中.
预定义的操纵算子
使用成员函数控制格式化输入输出时,每个函数调用需要写一条语句,尤其是它不能用在插入或提取运算符的表达式中,而使用操纵算子,则可以在插入和提取运算符的表达式中控制格式化输入和输出。在程序中使用操纵算字必须嵌入头文件iomanip.h
一般,预定义的控制符有以下几种:
dec 十进制的输入输出
hex 十六进制的输入输出
oct 八进制的输入输出
ws 提取空白字符
ends 输出一个nul字符
endl 输出一个换行字符,同时刷新流
flush 刷新流
resetiosflags(long) 请除特定的格式标志位
setiosflags(long) 设置特定的格式标志位
setfill(char) 设置填充字符
setprecision(int) 设置输出浮点数的精确度
setw(int) 设置域宽格式变量
其它流函数
在ios,istream和ostream类中,还定义了若干输入输出函数,它们主要用于错误处理,流的刷新以及流输入输出方式的控制.
错误处理
在对一个流对象进行I/O操作时,可能会产生错误。当错误发生时,错误的性质被记录在ios类的一个数据成员中。
ios类中定义的描述错误状态的常量:
goodbit-------------没有错误,正常状态
eofbit--------------到达流的结尾
failbit-------------I/O操作失败,清除状态字后,可以对流继续进行操作。
badbit--------------试图进行非法操作,清除状态字后,流可能还可以使用。
hardfail------------致命错误,不可恢复的错误。
对应于这些位,可用ios中定义的如下函数来检查流的当前状态位:
int good()---------如果正常,返回非0值
int bad()----------如果badbit被设置,返回非0值
int eof()----------如果eofbit被设置,返回非0值
int fail()---------如果failbit被设置,返回非0值
int rdstate()------返回当前错误状态位.
流的其它成员函数可以从流中读取字符或字符串,对流进行无格式化的输入 输出操作,以及直接控制对流的I/O操作。
返回类型 ostream类的成员 描 述
ostream& put(char ch) 向流中输出一个字符ch,不进行任何转换
ostream& write(char*,int) 向流中输出指定长度的字符串,不进行转换
ostream& flush( ) 刷新流,输出所有缓冲的但还未输出的数据
ostream& seekp(streampos) 移动流的当前指针到给定的绝对位置
ostream& seekp(sereamoff,seek_dir) 流的当前指针类似与文件的当前指针
streampos teelp( ) 返回流的当前指针的绝对位置
istream类的成员函数
返回类型 istream类的成员 描 述
int get( ) 读取并返回一个字符
istream& get(char&c) 读取字符并存入c中
istream& putback( ) 将最近读取的字符放回流中
istream& read(char*,int) 读取规定长度的字符串到缓冲区中
int peek( ) 返回流中下一个字符,但不移动文件指针
istream& seekg(streampos) 移动当前指针到一绝对地址
istream& seekg(streampos,seek_dir) 移动当前指针到一相对地址
streampos tellg( ) 返回当前指针
文件流
C++系统通过对流类进一步扩展,提供了支持文件I/O的能力,使得程序员在建立和使用文件时,就像使用cin和cout一样方便。下图新派生的五个类用于文件处理。fstreambase类提供了文件处理所需的全部成员函数,在它的派生类中没有提供新的成员函数。 ifstream类用于文件的输入操作;ofstream类用于文件的输出操作,fstream类允许对文件进行输入/输出操作。这几个类同时继承了前面介绍的流类的基本类等级中定义的成员函数。使用这些类时,必须在程序中嵌入头文件fstream.h通过打开一个文件,可将一个流与一个文件相联结。
filedbuf是streambuf的派生类,提供对文件缓冲区的管理能力。我们一般不涉及这个类C++系统通过对流类进一步扩展,提供了支持文件I/O的能力,这使得程序员在建立和使用文件时,就像使用cin和cout一样方便。左图新派生的五个类用于文件处理。fstreambase类提供了文件处理所需的全部成员函数,在它的派生类中没有提供新的成员函数。ifstream类用于文件的输入操作;ofstream类用于文件的输出操作,fstream类允许对文件进行输入/输出操作。这几个类同时继承了前面介绍的流类的基本类等级中定义的成员函数。使用这些类时,必须在程序中嵌入头文件fstream.h通过打开一个文件,可将一个流与一个文件相联结。在ios类中定义的一组枚举常量名给出了可允许的文件打开方式:
in----------------------- 打开一个文件进行操作
out---------------------- 打开一个文件进行写操作
ate---------------------- 文件打开时将文件指针指向文件尾
app---------------------- 添加,输出的内容添加到文件尾
trunc-------------------- 若文件存在,清除原有内容,将长度截为0
nocreat------------------ 若文件不存在,打开操作失败
文件的打开与关闭
在C++中,打开一个文件就是将这个文件与一个流建立关联,关闭一个文件就是取消这种关联。要执行文件的输入/输出,必须做三件事:
1)在程序中包含头文件ifstream.h
2)建立流。建立流的过程就是定义流类的对象,例如:
ifstream in;定义了输入流对象in
ofstream out;定义了输出流对象out
fstream io;定义了输入/输出流对象io
3)使用open( )函数打开文件,是某一文件与上面的某一流相联系。
open( )函数的原形为void open(const unsigned char*,int mod,int file_attrb);文件属性。取值为0代表普遍文件,1为隐藏文件等,缺省值为0。打开的文件使用完毕,必须将它关闭,关闭文件使用close( )函数,close( )函数也是流类中的成员函数。 打开一输入文件
ifstream input;
input.open("test1",ios::in,0);
打开一输出文件
ofstream output;
output.open("test2",ios::out,0);
打开一输入/输出文件
fstream both;
both.open("test",ios::in,ios::out,0);
可简化为:
fstream both("test",ios::in,ios::out,0);
关闭文件:both.close( );
文件流成员函数open()的格式如下所示:
void open(char * name,int mode,int file_attrb);
在这里name表示文件名,mode是下列值之一:
ios::app--------------------所有数据以附加方式写到流
ios::ate--------------------打开文件,并把文件指针移到文件尾
ios::in---------------------为读打开文件
ios::out--------------------为写打开文件
ios::trunc------------------如果文件存在,舍去文件内容
ios::nocreate---------------如果文件不存在,则失败
ios::noreplace--------------如果文件存在,则失败
文件的读写
C++把文件看作是字符序列,即所谓流式文件。根据数据的组织形式,文件又可分为ASCII码文件和二进制文件两种。如果要进行文件的输入输出,必须先建立一个流,然后将这个流与文件相关联,即打开文件,此时才能进行读写操作,完成后再关闭这个文件。如果建立的文件是供人阅读的,它必须以文本方式打开,如果是供其它程序使用,则可以使用二进制文件或文本文件。