流类库和输入输出

来源:互联网 发布:burberry围巾 知乎 编辑:程序博客网 时间:2024/05/01 01:50
 

12 流类库和输入输出

本章要点:

    1.流的概念及流类库

    2.输入输出的格式控制和ios 成员函数;

    3.输入输出运算符的重载;

    4.文件的输入输出。

 

12.1  输入/输出流的概念

    C++完全支持C的输入输出系统,但由于C的输入输出系统不支持类和对象,所以C++又提供了自己的输入输出系统,并通过重载运算符“<<”和“>>”来支持类和对象的输入输出。C++的输入输出系统是以字节流的形式实现的。

C++中的流是指数据从一个对象传递到另一个对象的操作。从流中读取数据称为提取操作,向流内添加数据称为插入操作。流在使用前要建立,使用后要删除。如果数据的传递是在设备之间进行,这种流就称为I/O流。C++专门内置了一些供用户使用的类,在这些类中封装了可以实现输入输出操作的函数,这些类统称为I/O流类。流具有方向性:与输入设备相联系的流称为输入流,与输出设备相联系的流称为输出流,与输入输出设备相联系的流称为输入输出流。

C++将一些常用的流类对象,如键盘输入、显示器输出、程序运行出错输出、打印机输出等,实现定义并内置在系统中,供用户直接使用。这些系统内置的用于设备间传递数据的对象称为标准流类对象,共有四个:

cin对象  与标准输入设备相关联的标准输入流。

cout对象  与标准输出设备相关联的标准输出流。

cerr对象  与标准错误输出设备相关联的非缓冲方式的标准输出流。

clog对象  与标准错误输出设备相关联的缓冲方式的标准输出流。

在缺省方式下,标准输入设备是键盘,标准输出设备是显示器,而不论何种情况,标准输出设备总是显示器。cin对象和cout对象前面已作过说明,cerr对象和clog对象都是输出错误信息,它们的区别是:cerr没有缓冲区,所有发送给它的出错信息都被立即输出;clog对象带有缓冲区,所有发送给它的出错信息都先放入缓冲区,当缓冲区满时再进行输出,或通过刷新流的方式强迫刷新缓冲区。由于缓冲区会延迟错误信息的显示,所以建议使用cout对象。

应注意的是,cout对象也能输出错误信息,但当用户把标准输出设备定向为其它设备时,cerr对象仍然把信息发送到显示器。

    这些标准流类对象都包含在头文件iostream.h中,使用时应包含该头文件。

12.2  流类库

    C++的流类库是用派生方法建立起来的输入输出类库,它有两个平行的基类streambufios,其它的流类都是从这两个基类直接或间接派生的。使用这些流类库时,必须包含相应的头文件。

    1.streambuf

    带有缓冲区的流类库,其作用是:

    提供物理设备的接口、缓冲或处理流的通用方式,几乎不需要任何格式。

体工队缓冲区的低级操作,如设置缓冲区,对缓冲区指针进行操作,从缓冲区取字符,向缓冲区存储字符等。

用作流类库中的基类,派生以下三个流类:

filebuf

使用文件来保存缓冲区中的字符序列。

strstreambuf

扩展类streambuf的功能,提供在内存进行提取和插入操作的缓冲区管理。

conbuf

扩展类streambuf的功能,用于处理输出,提供控制光标、设置颜色、定义活动窗口、清屏、清一行等功能,为输出操作提供缓冲区管理。

该类使用的缓冲区由一个字符序列和输入缓冲区指针与输出缓冲区指针组成,指针指向字符被取出或插入的位置。

通常情况下,均使用这三个派生类,很少直接使用streambuf类。

2.ios

ios类及其派生类为用户提供了使用流类的接口,它们均由一个指针指向streambuf类。ios类及其派生类使用streambuf及其派生类来完成对错误的格式化输入输出的检查,支持对streambuf类的缓冲区进行I/O时的格式化或非格式化转换。

ios作为流类库中的基类,可以派生出许多类,其类的层次关系如图12.1所示:

     ios

          istream

                 ifstream

                 ifstream

                 istream_withassign      iostream

                 istrstream                    fstream

              ostream                            strstream

                 ofstream                     sdiostream

        ostream_withassign

                 ostrstream

                     12.1  ios类的层次关系

流是一个抽象的概念,实际进行I/O操作时,必须将流与一种具体的物理设备联系起来。例如将流和键盘联系起来,当从该流中提取数据时,就是从键盘输入数据。用户也可以用istreamostream等类声明自己的流对象,例如:

istream is;  ostream os;

使用流类库的程序,不但可以识别标准的I/O设备,还可以重载运算符“<<”和“>>”,是程序识别用户自定义的类型,从而极大地提高了程序的可靠性和灵活性。

 

12.3  输入输出的格式控制

    C++仍可使用C中的printf()scanf()进行格式化控制,同时又提供了两种格式化控制的方法,一是使用ios类中的有关个是控制的成员函数,而是使用被称为格式控制符的特殊类型的函数。

12.3.1  ios类的成员函数进行格式控制

    此种成员函数进行格式控制主要是通过对格式状态字、域宽、填充字符和输出精度的操作来完成的。

    1.状态字

    也称状态标志字,其类型为long int。它是在ios类的public部分定义了一个枚举,此枚举类型的每个成员分别定义状态字的个位,每个位称为状态标志位。这个枚举的定义如下:

    enum

    {

      skipws       =ox0001    //跳过输入中的空白,用于输入

      left          =ox0002    //左对齐输出,用于输出

      light         =ox0004    //右对齐输出,用于输出

      internal       =ox0008    //在符号位和基指示符后填入字符,用于输出

      dec          =ox0010    //转换基数为十进制,用于输入或输出

      oct           =ox0020    //转换基数为八进制,用于输入或输出

      hex          =ox0040    //转换基数为十六进制,用于输入或输出

      showbase     =ox0080    //输出时显示基指示符,用于输入或输出

      showpoint    =ox0100    //输出时显示小数点,用于输出

      uppercase     =ox0200    //十六进制输出时,表示制式的和表示数值的

                               //一律为大写,用于输出

      showpos      =ox0400    //正整数前显示“+”符号,用于输出

      scientific      =ox0800    //用科学表示法显示浮点数,用于输出

      fixed         =ox1000    //用定点形式显示浮点数,用于输出

      unitbuf       =ox2000     //在输出操作后立即刷新所有流,用于输出

      stdio         =ox4000    //在输出操作后刷新stdoutstderr,用于输出

    }

    这些枚举元素的值的共同特点是,使状态标志字二进制表示中的不同位为1,它们共同组成状态标志字,存放在数据成员long x_flags中。这些状态之间是活的关系,可以几个并存。

    2.ios类的成员函数进行格式控制

    ios类提供了几个用于控制输入输出格式的成员函数,其中的参数flags是状态控制字,存放在ios类中的数据成员long x_flags。主要成员函数的使用方法如下:

    设置状态标志

是指用函数setf()将状态字标志位置“1”,函数原型为:

long ios::setf(long flags)

使用时的一般调用形式为:

流对象.setf(ios::状态标志)

例如:

istream isobj;

ostream osobj;

isobj.setf(ios::skipws);

osobj.setf(ios::right|ios::showpos|ios::dec);

清除状态标志

是指用函数unsetf()将某一状态标志置“0”,函数原型为:

long ios::unsetf(long flags)

使用时的一般调用形式为:

流对象.unsetf(ios::状态标志)

取状态标志

是指用函数flags()返回状态标志字,有带参数和不带参数两种形式,它们的函数原型为:

long ios::flags()

long ios::flags(long flags)

使用时的一般调用形式为:

流对象.flags()

流对象.flags(ios::状态标志)

不带参数时返回当前的状态标志字;带参数时将状态字设置flags,并返回设置前的状态标志字。flgs()setf()的区别是:setf()是在原有的基础上追加设置,不改变原有设置;flgs()使用新的设置覆盖原有的设置,改变了原有设置。

设置域宽

域宽是指输出字符的长度。域宽的设置用函数width()完成,有带参数和不带参数两种形式,它们的函数原型为:

long ios::width()

long ios::width(int w)

使用时的一般调用形式为:

流对象.width()

流对象.width(int w)

不带参数时返回当前的域宽值;带参数时将域宽值设置为w,并返回设置前的域宽值。域宽的值存放在ios类中的数据成员int x_width中。

设置显示精度

是指用函数precision()设置浮点数输出时的显示精度,函数原型为:

long ios:: precision(int p)

使用时的一般调用形式为:

流对象. precision(int p)

设置的显示精度的值存放在ios类中的数据成员int x_precision中。

填充字符

是指当输出值少于域宽时将剩余部分用设定的填充字符填满,却省时的填充字符为空格。设置填充字符的函数原型有带参数和不带参数两种形式,它们为:

long ios::fill()

long ios::fill(char ch)

使用时的一般调用形式为:

流对象.fill()

流对象.fill(char ch)

不带参数时返回当前的填充字符;带参数时将填充字符设置为ch,并返回设置前的填充字符。设置的填充字符存放在ios类中的数据成员int x_fill中。

注意,使用填充字符函数时,必须与设置域宽函数配合使用,否则没有意义。

12.1 使用成员函数设置状态字的例。

#include<iostream.h>

void dispflags(long f)

{

  long I;

  for(i=ox8000;i;i=i>>1)

    { 

      if (i&f)

        cout<<”1”;

      else

        cout<<”0”;

    }

  cout<<endl;

}

main()

{

  long f;

  dispflags(f);

  out.setf(ios::showpos|ios::scientific);

f=cout.flags();

dispflags(f);

  out.unsetf(ios::scientific);

f=cout.flags();

dispflags(f);

f=cout.flags(ios::oct);

dispflags(f);

f=cout.flags();

dispflags(f);

  return 0;

}

运行结果:

      0010000000000001    //显示缺省状态下的状态标志

      0010110000000001    //显示执行setf()函数后的状态标志

      0010010000000001    //显示执行unsetf()函数后的状态标志

      0010010000000001    //显示执行flags(ios::oct)函数前的状态标志

      0000000000100000    //显示执行flags(ios::oct)函数后的状态标志

注意,flags(long flags)函数返回的是执行该函数前的状态标志。

12.2 使用成员函数控制输入输出格式的例。

#include<iostream.h>

main()

{

cout<<”x_width=”<<cout.width()<<endl;

cout<<”x_fill=”<<cout.fill()<<endl;

cout<<”x_precision=”<<cout.precision()<<endl;

cout<<123<<”    “<<123.456789<<endl;

cout<<”----------------------------------------------“<<endl;

cout.width(10);

cout.precision(3);

  cout<<123<<”    “<<123.456789<<endl;

  cout<<”x_width=”<<cout.width()<<endl;

  cout<<”x_fill=”<<cout.fill()<<endl;

  cout<<”x_precision=”<<cout.precision()<<endl;

  cout<<”----------------------------------------------“<<endl;

cout.width(10);

cout.fill(*);

  cout<<123<<”    “<<123.456789<<endl;

cout.width(10);

cout.setf(ios::left);

  cout<<123<<”    “<<123.456789<<endl;

  cout<<”x_width=”<<cout.width()<<endl;

  cout<<”x_fill=”<<cout.fill()<<endl;

  cout<<”x_precision=”<<cout.precision()<<endl;

return 0;

}

运行结果:

      x_width=0

      x_fill=

      x_precision=0

      123    123.456789

---------------------------------

             123    123.457

      x_width=10

      x_fill=

      x_precision=3

---------------------------------

      *******123    123.457

      123*******    123.457

      x_width=10

      x_fill=*

      x_precision=3

12.3.2  使用格式控制符进行格式控制

    显然,使用成员函数控制输入输出格式时,每个函数的调用都要写一条语句,它们还不能直接嵌入到输入输出语句中,使用很不方便。为此,C++由提供了另外一种输入输出格式的控制方法,这就是使用称为格式控制符的特殊函数。

    1.预定义的格式控制符

    这些预定义的格式控制符可以直接嵌入到输入输出语句中,完成类似于ios类中控制输入输出格式的成员函数的功能。预定义的格式控制符如下表所示:

12.1 预定义的格式控制符

     格式控制符                                 

  dec                以十进制形式输入输出整型数,用于输入或输出

  hex                以十六进制形式输入输出整型数,用于输入或输出

  oct                以八进制形式输入输出整型数,用于输入或输出

  ws                用于输入时跳过开头的空白符,仅用于输入

  endl               插入一个换行符并刷新输出流,仅用于输出

  ends               插入一个空字符,用来结束一个字符串,仅用于输出

  flush               刷新一个输出流,仅用于输出

  setbase(int n)        把转换基数设置位n(n=0,8,10,16),缺省值为0(十进制)

  resetiosflags(long f)   关闭由参数f指定的格式标志,用于输入或输出

  setiosflags(long f)    设置由参数f指定的格式标志,用于输入或输出

  setfill(int c)         设置c为填充字符,缺省为空格,用于输入或输出

  setprecision(int n)    设置小数位数,缺省为6位,用于输入或输出

  setw(int n)          设置域宽,用于输入或输出

 

    格式控制符setiosflags(long f)resetiosflags(long f)中的格式标志与ios类中控制输入输出格式的成员函数所用的标志基本相同,这里不再说明。

    2.预定义格式控制符的使用

    预定义格式控制符分为带参数和不带参数的两种,带参数的在头文件iomanip.h中定义,不带参数的在头文件iostream.h中定义。使用它们时,程序中应包含相应的头文件。

    格式控制符被嵌入到输入输出语句中控制输入输出的格式,而不是执行输入输出操作。

12.3 使用预定义的格式控制符控制输入输出格式的例。

#include<iostream.h>

#include<iomahip.h>

main()

{

cout<<setw(10)<<987<<654<<<<endl;

cout<<987<<setiosflags(ios::scientific)<<setw(15)<<987.654321<<endl;

cout<<987<<setw(10)<<hex<<987<<endl;

cout<<987<<setw(10)<<oct<<987<<endl;

cout<<987<<setw(10)<<setbase(0)<<987<<endl;

cout<<resetiosflags(ios::scientific)<<setprecision(3)<<987.654321<<endl;

cout<<setiosflags(ios::left)<<setfill(‘&’)<<setw(7)<<987<<endl;

cout<<setiosflags(ios::right)<<setfill(‘#’)<<setw(7)<<987<<endl;

return 0;

}

运行结果:

          987654

      987   9.876543e+02

      987       3db

      3db      1733

1733       987

      9.877e+02

      987&&&&

      ####987

有例可见,格式控制符setw只对最靠近它的输出起作用,格式控制符decocthex的作用一直保持到重新设置为止,。格式控制符setprecision在输出时作四舍五入处理。

    3.自定义的格式控制符

    予定义的格式控制符外,用户还可自定义格式控制符。

为输出流自定义格式控制符的一般形式为:

    ostream &格式控制符名(ostream &stream)

    {

      //自定义代码

      return stream;

    }

为输入流自定义格式控制符的一般形式为:

    istream &格式控制符名(istream &stream)

    {

      //自定义代码

      return stream;

    }

    上述定义中,格式控制符名和格式控制符代码由用户给出,stream可使用其它标识符外,其余的都照原样写上。特别注意不要丢了返回语句

return stream;

这是自定义格式控制符的关键。

12.4 使用自定义格式控制符控制输入输出格式的例。

#include<iostream.h>

#include<iomahip.h>

ostream &output1(ostream &out)

{

  out.setf(ios::left);

  out<<setw(10)<<setbase(16)<<setfill(‘*’);

  return out;

}

main()

{

cout<<345<<endl;

cout<<out<<345<<endl;

return 0;

}

运行结果:

      345

      159*******

 

12.4  用户自定义数据类型的输入输出

    用户自定义数据类型的输入输出,是通过重载运算符“<<”和“>>”实现的。

12.4.1  重载输出运算符“<<

    重载输出运算符“<<”也称为插入运算符,用以用户自定义类型的输出。

    定义运算符“<<”重载函数的一般形式为:

    ostream &operator<<(ostream &stream,类名 对象名)

    { 

      //操作代码

      return stream;

    }

其中,第一个参数stream是对ostream对象的引用,必须是输出流,它可以是其它合法的标识符,但必须与return后面的标识符相同。第二个参数接受将被输出的对象。

    重载输出运算符“<<”使用中的问题:

    重载输出运算符函数不能是类的成员函数,必须是类的非成员函数。

作为非成员函数,重载输出运算符函数不能访问类的私有成员,为解决这个问题,应把重载输出运算符函数定义为类的友元函数。

12.5 重载输出运算符“<<”的例。

#include<iostream.h>

class point

{

  int x,y;

  public:

    point()

    {  x=0; y=0;  }

    point(int xx,int yy)

    {  x=xx; y=yy;  }

    friend ostream &operator<<(ostream &stream, point obj);

};

ostream &operator<<(ostream &stream, point obj)

{

  stream<<”x=”<<obj.x<<”    “<<”y=”<<obj.y<<endl;

  return stream;

}

main()

{

point p1(1,2),p2(3,4);

cout<<p1<<p2;

return 0;

}

运行结果:

      x=1    y=2

      x=3    y=4

12.4.2  重载输入运算符“>>

    重载输出运算符“>>”也称为提取运算符,用以用户自定义类型的输入。

    定义运算符“>>”重载函数的一般形式为:

    istream &operator>>(istream &stream,类名 &对象名)

    { 

      //操作代码

      return stream;

    }

其中,第一个参数stream是对istream对象的引用,必须是输出流,它可以是其它合法的标识符,但必须与return后面的标识符相同。第二个参数是一个引用,前面的“&”不能省掉。。

    重载输出运算符“>>”使用中的问题:

    重载输出运算符函数不能是类的成员函数,必须是类的非成员函数。

作为非成员函数,重载输出运算符函数不能访问类的私有成员,为解决这个问题,应把重载输出运算符函数定义为类的友元函数。

12.5 重载输出运算符“>>”的例。

#include<iostream.h>

class point

{

  int x,y,;

  public:

    point()

    {  x=0; y=0;  }

    point(int xx,int yy)

    {  x=xx; y=yy;  }

    friend ostream &operator<<(ostream &output, point obj);

    friend istream &operator<<(istream &input, point &obj);

};

ostream &operator<<(ostream &output, point obj)

{

  output<<”x=”<<obj.x<<”    “<<”y=”<<obj.y<<endl;

  return output;

}

ostream &operator>>(ostream &output, point &obj)

{

  cout<<请输入x,y的值:”<<endl;

  input>>obj.x;

  input>>obj.y;

  return input;

}

main()

{

point p(10,20);

cout<<p;

cin>>p;

cout<<p;

return 0;

}

运行结果:

      x=10    y=20

      请输入x,y的值:30  40

x=30    y=40

 

12.5  文件的输入输出

    文件是一系列字符数据的有序集合,按组织形式可分为文本文件和二进制文件两种。C++的文件把数据看作是一连串的字符,不考虑纪录的界限,认为它是一个字符流或二进制流,称它为流式文件,增加了处理的灵活性。

    C++中,要进行文件的输入输出,必须先创建一个流,再把这个流与文件相关联,即打开文件,才能进行输入输出操作,完成后要关闭文件。

12.5.1  文件的打开与关闭

    1.输入输出流类

    为了执行文件的输入输出操作,C++提供了三个输入输出流类:

    ofstream由基类 osream派生而来,用于文件的输出(写)。

    ifstream由基类 isream派生而来,用于文件的输入(读)。

    fstream由基类 iosream派生而来,用于文件的输入或输出。

    他们同属于ios类,可访问在ios类中定义的所有操作。

与此相对应,为了执行文件的输入输出操作,C++还提供了三个输入输出流,即输入流、输出流和输入输出流。建立流就是定义流类的对象,例如:

ofstream  out;

ifstream  in;

fstream  inout;

建立了流以后,就可以把某一个流与文件建立联系,进行文件的读写操作了。

2.文件的打开

打开文件,就是用函数open()把某一个流与文件建立联系。open()函数是上述三个流类的成员函数,定义在fstream.h头文件中,它的原型为:

void open(const unsigned char *,int mode,int dcces=filebuf::openprot);

其中:第一个参数用来传递文件名;

第二个参数的值决定文件打开的方式,必须从下列值中选取:

    ios::app               //使输出追加到文件尾部,只用于输出

    ios::ate                //查找文件尾

    ios::in                 //打开一个文件进行读操作,只用于输入

    ios::nocreate            //文件不存在,导致open()失败

    ios::noreplace           //若文件存在,则open()失败

    ios::out                //打开一个文件进行写操作,只用于输出

    ios::trunc               //删除同名文件

    ios::binary              //以二进制方式打开文件,缺省为文本方式

以上各值可以组合使用,之间用“|”分开。

第三个参数的值决定文件的访问方式及文件的类别,缺省方式是filebuf::openprot。在DOS/Windows环境中,access的值对应DOS/Windows的文件属性代码:

    0      普通文件

    1      只读文件

    2      隐含文件

    3      系统文件

    8      备份文件

    3.文件的关闭

    文件使用完后,必须关闭,否则会丢失数据。

关闭文件就是将文件与流的联系断开。关闭文件用函数close()完成,它也是流类中的成员函数,没有参数,没有发返回值。

12.6 文件打开关闭的例。

#include<iostream.h>

#include<fstream.h>

void main()

{

  ifstream in;

  in.open(“c://myfile”,ios::nocreate);

  if (in.fail())

    cout<<“文件不存在,打开失败!”<<endl;

  in.close();

}

本例中的函数fail()是流类中的成员函数,当文件以ios::nocreate方式打开时,可用该函数测试文件是否存在。若存在,返回0,否则,返回非0。还有其他一些成员函数,请参考有关文献。说明打开文件的路径时,反斜杠要双写,因为编译器认为反斜杠是转义字符标志。

可将定义流与打开文件用一条语句完成,如:

ofstream out(“test”,ios::out,0); 

fstream io(“test”,ios::in|ios::out,0);

一般情况下,ifstreamofstream流类的析构函数就可以自动关闭已打开的文件,但若需要使用同一个流对象打开的文件,则需要首先用close()函数关闭当前文件。

12.5.2  文件的读写

    在含有文件操作的程序中,必须包含头文件fstream.h

    1.文本文件的读写

    对文本文件进行读写时,先要以某种方式打开文件,然后使用运算符“<<”和“>>”进行操作就行了,只是必须将运算符“<<”和“>>”前的cincout用与文件相关联的流代替。

12.7 文件读写的例。

#include<fstream.h>

int main()

{

  ofstream fout(“test”);

  if(!fout)

{

cout<<“不能打开输出文件。”<<endl;

return 1;

}

      fout<<“你好!”<<endl;

      fout<<10<<”    “<<hex<<10<<endl;

      fout.close();

      istream fin(“test”);

  if(!fin)

{

cout<<“不能打开输入文件。”<<endl;

return 1;

}

  int i;

char c[20];

fin>>c>>i;

cout<<c<<”    “<<i<<”    “<< endl;

fin.close();

return 0;

}

运行结果:

    你好!

    10

    2.二进制文件的读写

    文本文件与二进制文件的区别

文本文件是字符流,二进制文件是字节流。

文本文件在输入时,将回车和换行两个字符转换为字符“/n”,输出是在将字符“/n”转换为回车和换行两个字符,二进制文件不做这种转换。

文本文件遇到文件结束符时,用get()函数返回一个文件结束标志EOF,该标志的值为-1。二进制文件用成员函数eof()判断文件是否结束,其原型为:

int eof();

当文件到达末尾时,它返回一个非零值,否则返回零。当从键盘输入字符时,结束符为ctrl_z,也就是说,按下ctrl_zeof()函数返回的值为真。

二进制文件的读写

任何文件,都能以文本方式或二进制方式打开。对以二进制方式打开的文件,由两种方式进行读写操作,一种是使用函数get()put(),另一种是使用函数read()write()

使用函数get()put()读写二进制文件

get()函数 该函数是输入流类istream中定义的成员函数,作用是从与流对象连接的文件中读出数据,其原型为(它有许多格式,这里只介绍最一般的格式):

istream &get(unsigned char &ch);

它从流中每次读出一个字节或一个字符放入引用ch&中。

put()函数 该函数是输出流类ostream中定义的成员函数,作用是向与流对象连接的文件中写入数据,其原型为(它有许多格式,这里只介绍最一般的格式):

istream &put( char ch);

它将一个字节或一个字符写入流中。

    12.8 用函数get()put()读写二进制文件的例。

#include<iostream.h>

#include<fstream.h>

main(int argc,char *argv[])

{

  char ch;

if (argc !=3)

{

cout<<“命令行输入错误!”<<endl;

return 1;

}

ifstream fin(argv[1]);

  if(!fin)

{

cout<<“不能打开源文件。”<<endl;

return 1;

}

ofstream fout(argv[2]);

  if(!fout)

{

cout<<“不能打开目标文件。”<<endl;

return 1;

}

      while(fin)

      {

fin.get(ch);

        fout.put(ch);

      }

      fin.close();

  fout.close();

return 0;

}

该程序的运行结果是把文件1的内容拷贝到文件2

    使用函数read()write()读写二进制文件

read()函数 该函数是输入流类istream中定义的成员函数,其最常用的原型为:

istream &read(unsigned char *buf,int num);

作用是从相应的流读出num字节或字符的数据,把他们放入指针所指向的缓冲区中。第一个参数buf是一个指向读入数据存放空间的指针,它是读入数据的起始地址;第二个参数num是一个整数值,该值说明要读入数据的字节或字符数。该函数的调用格式为:

    read(缓冲区首地址,读入的字节数)

    注意:“缓冲区首地址”的数据类型为unsigned char *,当输入其他类型数据时,必须进行类型转换。

write()函数 该函数是输出流类ostream中定义的成员函数,其最常用的原型为:

ostream &write(const unsigned char *buf,int num);

作用是从buf所指向的缓冲区把num字节的数据写到相应的流中。参数的含义、调用及注意事项与read()相同。

    12.9 用函数read()write()读写二进制文件的例。

#include<iostream.h>

#include<fstream.h>

#include<string.h>

main()

{

  ofstrem outf(“test”)

if (! outf)

{

cout<<“不能打开输出文件!”<<endl;

return 1;

}

double n=123.44;

char str[]=“向文件写入双精度数和字符串”;

outf.write((char *)&n,sizeof(double));

outf.write(str,strlen(str));

outf.close();

  ifstrem inf(“test”)

if (! inf)

{

cout<<“不能打开输入文件!”<<endl;

return 1;

}

double n;

char ch[30];

inf.read((char *)&n,sizeof(double));

inf.read(str,26);

cout<<n<<”    “<<str<<endl;

inf.close();

return 0;

}

运行结果:

    123.44    向文件写入双精度数和字符串

    需要说明的是,上述四个函数get()put()read()write()也可以用于文本文件,其处理过程与二进制文件的处理过程基本相同,这里不再介绍。

    3.文件的随机读写

    前面介绍的文件的读写操作,都是按一定的顺序进行读写的,称为顺序文件,他们只能按数据在文件中的排列顺序一个一个地访问数据,使用很不方便。为此,C++又提供了文件的随机读写。它是通过使用输入或输出流中与随机移动文件指针相关的成员函数,随意移动文件指针而达到随机访问的目的。

    移动文件指针的成员函数主要有seekg()seekp(),它们的常用原型为:

    isream &seekg(streamoff offset,seek_dir origin);

    osream &seekp(streamoff offset,seek_dir origin);

其中,参数origin表示文件指针的起始位置,offset表示相对于这个起始位置的位移量。seek_dir是系统定义的枚举名,origin是枚举变量。origin的取值由三种情况:

    ios::beg  从文件头开始,把文件指针移动由offset指定的距离。

ios::cur  从文件当前位置开始,把文件指针移动offset指定的距离。

ios::end  从文件尾开始,把文件指针移动由offset指定的距离。

显然,当origin的值为ios::beg时,offset的值为正;当origin的值为ios:: cur时,offset的值为负;当origin的值为ios::end时,offset的值可正可负;正数时从前向后移动文件指针,负数时从后向前移动文件指针。

位移量offset的类型是streamoff,该类型在头文件iostream.h中定义如下:

typedef streamoff long;

即为long型。

    函数seekg()用于输入文件,将文件的读指针从origin说明的位置移动offset字节;函数seekp()用于输出文件,将文件的写指针从origin说明的位置移动offset字节。

    进行文件的随机读写时,可用下列函数确定文件当前指针的位置:

streampos tellg();

streampos tellp();

其中,streampos是在头文件iostream.h中定义的类型,实际是long型的。函数tellg()用于输入文件,函数tellp()用于输出文件。

    12.10 文件随机读写的例。

#include<fstream.h>

#include<stdlib.h>

main(int argc,char *argv[])

{

 

chr ch;

if (argc !=3)

{

cout<<“命令行输入错误!”<<endl;

return 1;

}

ifstrem fin(argv[1])

if (! fin)

{

cout<<“不能打开输入文件!”<<endl;

return 1;

}

fin.seekg(atoi(argv[2],ios::beg)

while(! Fin.eof())

{

fin.set(ch);

cout<<ch;

}

fin.close();

return 0;

}

设本程序的执行文件名为E7_10.exe,要现实的文件名为test,想从第3个位置其开始显示,则执行的命令为:

    c:/E7_10 test 3

 

12.6  应用举例

    12.11 重载运算符“<<”和“>>”,使之能直接输入输出复数。

    #include<iostream.h>

    class complex

    {

      float real,imag;

      public:

        complex()

        {  real=0; imag=0;  }

        complex(float x,float y)

        {  real=x; imag=y;  }

        friend complex operator+(complex,complex);

        friend ostream &operator<<(ostream &,complex &);

        friend istream &operator>>(istream &,complex &);

    };

    complex operator+(complex a,complex b)

    {

      complex t;

      t.real=a.real+b.real’

      t.imag=a.imag+b.imag;

      return t;

}

ostream &operator<<(ostream &out,complex &obj)

{

  out<<obj.real;

  if(obj.imag >0)

   out<<”+”;

  if(obj.imag !=0)

   out<<obj.imag<<”i”;

  return out;

}

istream &operator>>(istream &in,complex &obj)

{

  cout<<“请输入复数的实部和虚部:”<<endl;

  in>>obj.real;

  in>>obj.imag;

  return in;

}

main()

{

  complex p1,p2,p3;

  cin>>p1;

  cout<<“复数p1的值是:”<<p1<<endl;

  cin>>p2;

  cout<<“复数p2的值是:”<<p2<<endl;

  p3=p1+p2;

  cout<<“复数p3的值是:”<<p3<<endl;

  return 0;

}

程序运行后屏幕显示为:

    请输入复数的实部和虚部:

此时若输入:1.2  3.4

屏幕又显示:

    复数p1的值是:1.2+3.4i

屏幕再次显示:

    请输入复数的实部和虚部:

此时若输入:5.6  5.4

屏幕最后显示:

    复数p2的值是:5.6+5.4i

复数p3的值是:6.8+8.8i