C++程序设计第9章 流类库和输入输出1_第1页
C++程序设计第9章 流类库和输入输出1_第2页
C++程序设计第9章 流类库和输入输出1_第3页
C++程序设计第9章 流类库和输入输出1_第4页
C++程序设计第9章 流类库和输入输出1_第5页
已阅读5页,还剩77页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

没有虚函数时的继承:1)虚函数按照其声明顺序放于表中。2)父类的虚函数在子类的虚函数前面。

实现虚函数需要对象附带一些额外信息,以使对象在运行时可以确定该调用哪个虚函数。对大多数编译器来说,这个额外信息的具体形式是一个称为vptr(虚函数表指针)的指针。vptr指向的是一个称为vtbl(虚函数表)的函数指针数组。每个有虚函数的类都附带有一个vtbl。当对一个对象的某个虚函数进行请求调用时,实际被调用的函数是根据指向vtbl的vptr在vtbl里找到相应的函数指针来确定的。1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。2)没有被覆盖的函数依旧。Base*b=newDerive();b->f();由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。一般继承(有虚函数覆盖)多重继承(无虚函数覆盖)1)每个父类都有自己的虚表。2)子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。多重继承(有虚函数覆盖)三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,就可以任一静态类型的父类来指向子类,并调用子类的f()了。例:定义一个抽象类shape用以计算面积,从中派生出计算圆、矩形和三角形面积的派生类。constdoublePI=3.1415926;classshape{//抽象基类public: virtualvoiddisplay()=0; virtualdoubleArea()=0;//求面积};classcircle:publicshape{ protected: doubler;public: circle(doublex=0){r=x;}voiddisplay() {cout<<"圆半径

"<<r<<endl; }doubleArea(){returnPI*r*r;}};classrectangle:publicshape{

doublel,w;public: rectangle(doublex=0,doubley=0){

l=x;

w=y;

}

voiddisplay(){

cout<<"长:"<<l<<"\t宽:"<<w<<endl;

}

doubleArea(){

returnl*w;

}};classtriangle:publicshape{ doublea,b,c;public:triangle(doublea1=0,doubleb1=0,doublec1=0){a=a1;b=b1;c=c1;}voiddisplay(){cout<<"三角形边:"<<a<<''<<b<<''<<c<<endl;}doubleArea(){ doubles=(a+b+c)/2; returnsqrt(s*(s-a)*(s-b)*(s-c));}};intmain(){ shape*S; S=newcircle(10); S->display(); cout<<"圆面积为:"<<S->Area()<<endl; S=newrectangle(3,5); S->display();cout<<“矩形面积为:"<<S->Area()<<endl;

S=newtriangle(3,4,5); S->display();

cout<<“三角形面积为:"<<S->Area()<<endl; return0;}第九章流类库和输入/输出本章的最主要目的就是把对象保存到磁盘文件中并从磁盘文件重建对象。C++语言中并没有输入/输出语句,而是在标准库里包含了一个I/O流类库,它与标准模板库同为C++标准库中最重要的组成部分。数据从一个对象到另一个对象的传送被抽象为“流”。数据的输入/输出就是通过输入/输出流来实现的。流是一种抽象的概念,负责在数据的产生者和数据的使用者之间建立联系,并管理数据的流动。

多数应用程序都要求输入与输出。输入数据通常来自键盘的击键信息、磁带或磁盘上的文件;输出数据通常是显示在屏幕上或保存在磁盘或磁带的文件中。当程序要求输出数据时当程序要求输入数据时

数据流简称为流,是字节或字符的有序序列。

输入流对象:提供数据从源点流向程序的通道,程序可以从输入流对象读取字节或字符。输出流对象:提供数据从程序流向数据终点的通道,程序可向输出流对象写入字节或字符。

C++用输入流对象和输出流对象来实现数据流。

从流中取得数据的操作称为提取操作(>>),向流中添加数据的操作称为插入操作(<<)。C++提供了两种类型的流:文本流和二进制流。文本流是一串ASCII字符。(如源程序文件和文本文件,文字处理软件产生的数据文件),文本流可以直接输出到显示器或送到打印机上打印。二进制流是将数据以二进制形式存放的,这种流在数据传输时不需作任何变换。C++的输入与输出包括以下3方面的内容:(1)对系统指定的标准设备的输入和输出。(标准I/O)(2)以外存磁盘文件为对象进行输入和输出。(文件I/O)(3)对内存中指定的空间进行输入和输出。(串I/O)最重要的三个输出流是:

ostream

//通用输出流类

ofstream

//输出文件流类

ostrstream

//输出字符串流类9.1C++的基本流类体系

ios为抽象类,析构函数是虚函数,标准C中构造函数为保护的;而VC++中有一个构造函数ios(streambuf*)为公有。图9.1输入/输出流类派生体系iosstreambufistreamostreamistream_withassigniostreamostream_withassigniostream_withassign指针成员用来管理缓冲区

在C++中,输入输出流被定义为类。C++的I/O库中的类称为流类(streamclass)。用流类定义的对象称为流对象。

cout和cin是iostream类的对象,1.iostream类库中有关的类C++编译系统提供了用于输入输出的iostream类库。在iostream类库中包含许多用于输入输出的类。2.与iostream类库有关的头文件#include命令包含了有关的头文件就相当于在本程序中声明了所需要用到的类,头文件是程序与类库的接口。iostream类库的接口分别由不同的头文件来实现。常用的有:iostream包含了对输入输出流进行操作所需的基本信息。fstream用于用户管理的文件的I/O操作。strstream用于字符串流I/O。stdiostream用于混合使用C和C++的I/O机制时。iomanip在使用格式化I/O时应包含此头文件。3.在iostream头文件中定义的流对象C++流类库中定义了四个全局流对象:cin、cout、cerr、clog注:使用这些对象时必须包含iostream头文件。cin是istream的派生类istream_withassign的对象,称cin流或标准输入流。从标准输入设备(键盘)输入到内存的数据流。cout是ostream的派生类ostream_withassign的对象,称为cout流或标准输出流,从内存输入到标准输出设备(显示器)的数据流。cerr和clog作用相似,是ostream的派生类ostream_withassign的对象,称其为标准错误输出流。cin、cout、clog带缓冲区,缓冲区由streambuf类对象来管理;cerr不带缓冲区,一旦错误发生立即显示。4.在iostream头文件中重载运算符

在istream和ostream类中分别有一组成员函数对位移运算符“<<”和“>>”进行重载,以便能用它输入或输出各种标准数据类型的数据。对于不同的标准数据类型要分别进行重载。如:ostreamoperator<<(int);//用于向输出流插入一个int数据ostreamoperator<<(float);

//用于向输出流插入一个float数据ostreamoperator<<(char);

//用于向输出流插入一个char数据ostreamoperator<<(char*);

//用于向输出流插入一个字符串数据。标准输出流标准输出流指:流向标准输出设备(显示器)的数据。cout,cerr和clog流1.cout流对象(标准输出流)说明:cout不是C++预定义的关键字,是ostream流类的对象,在iostream中定义。(2)用“cout<<”输出基本类型的数据时,可不必考虑数据的类型,系统会判断数据的类型,并根据其类型选择调用与之匹配的运算符重载函数。(3)cout流在内存中对应开辟了一个缓冲区,用来存放流中的数据,当向cout流插入一个endl时,不论缓冲区是否已满,都立即输出流中所有数据,然后插入一个换行符,并刷新流(清空缓冲区)。(4)在iostream中只对“<<”和“>>”运算符用于标准类型数据的输入输出进行了重载,未对用户声明的类型数据的输入输出进行重载。2.cerr流对象(标准错误输出流)作用:向标准错误设备输出有关出错信息。与cout流的区别:

cout流通常是传送到显示器输出,也可以被重定向输出到磁盘文件,而cerr流中的信息只能在显示器输出。3.clog流对象(标准错误流)作用:和cerr相同,都是在终端显示器上显示出错信息。区别:cerr是不经过缓冲区,直接向显示器上输出有关信息,clog中的信息存放在缓冲区中,缓冲区满后或遇endl时向显示器输出。9.3标准设备的输入/输出标准设备输入需注意以下几点,以避免错误:1.cin为缓冲流。键盘输入的数据保存在缓冲区中,程序从缓冲区中逐个提取数据。只有缓冲区中数据用完才会再次要求输入数据,而不可能用刷新来清除缓冲区。3.输入的数据类型应当与要提取的数据类型一致或相容,否则出错。而出错只是对流的状态字state的对应位置位(置1),程序将继续运行。所以要提高健壮性,就必须在编程中加入对状态字state的判断。2.空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入。但如果是字符型和字符串,则空格(ASCII码为32)无法用cin输入,字符串中也不能有空格。回车符也无法读入。与格式控制类似,ios中说明中说明了一个枚举类型来描述输入输出的状态状态字state为整型,其它的各位在ios中说明:enumios_state{

goodbit=0x00,

//流正常

eofbit=0x01,

//输入流结束忽略后继提取操作;或文件结束已无数据可取

failbit=0x02,

//最近的I/O操作失败,流可恢复

badbit=0x04,

//最近的I/O操作非法,流可恢复

hardfail=0x08

//I/O出现致命错误,流不可恢复,VC++不支持

};ios中定义一个整型数据成员state来记录状态,通过一些接口函数来读取和设置输入输出的状态。读取状态的有关操作如下:inlineintios::rdstate()const{returnstate;}

//读取状态字inlineintios:operator!()const{returnstate&(badbit|failbit);}

//可用操作符!()代替fail()inlineintios::bad(){returnstate&badbit;}

//返回非法操作位inlinevoidios::clear(int_i){lock();state=_i;unlock();}

//人工设置状态,可用来清状态inlineintios::eof()const

{returnstate&eofbit;}

//返回流(文件)结束位inlineintios::fail()const{returnstate&(badbit|failbit);}

//返回操作非法和操作失败这两位inlineintios::good()const{returnstate==0;}

//正常返回1,否则返回0【例9.3】提高输入的健壮性。intmain(){

charstr[256];inti;cout<<"请输入整数:"<<endl;cin>>i;

//可故意输入若干非数字字符,下次再输入若干字符加数字串加若干非数字字符进行检测

while(cin.fail()){//键盘操作非法和操作失败

cout<<"状态字为:"<<cin.rdstate()<<endl;//读取状态字

cin.clear(0);//清理状态

cin.getline(str,255);//读空缓冲区(包括回车)

cout<<"输入错误,请重新输入整数"<<endl;cin>>i;}

cin.getline(str,256);//读空缓冲区(包括回车符)

cout<<"请输入字符串"<<endl;cin.getline(str,255);//读字符串

cout<<"输入整数为:"<<i<<endl;cout<<"输入字符串为:"<<str<<endl;

return0;}函数getline()作用:从输入流中读取一行字符。

格式:cin.getline(字符数组(或字符指针),字符个数n,终止标志字符)(1)插入符和提取符应重载为类的友元。(2)插入符和提取符重载格式如下:ostream&operator<<(ostream&s,consttype&p){ //操作代码

returns;}istream&operator>>(istream&s,type&p){ //操作代码

returns;}

其中左操作数是对ostream或istream对象的引用,右操作数接收将被输出或输入的对象。(3)对重载的插入符或提取符的调用形式如下:

ostream<<obj;istream>>obj;9.4

文件的输入与输出文件:

指存储在外部介质上数据的集合。一批数据是以文件的形式存放在外部介质上的。本节中文件主要指的是磁盘文件。文件中数据的组织形式:

可分为ASCII文件和二进制文件。字符数据:

在内存中是以ASCII代码形式存放的,因此,无论用ASCII文件输出还是用二进制文件输出,其数据形式是一样的。数值数据:

在内存中存放是不同的(存放字节不同)。例:有一个长整数100000,在内存中占4个字节,如果按内部格式直接输出,在磁盘文件中占4个字节,如果将它转换为ASCII码形式输出,则要占6个字节。C++根据文件内容的数据格式,可分为两类:

文本文件、二进制文件。1000004948484848480000000000000001100001101010000000110001001100000011000000110000001100000011000000000000000000011000011010100000内存中存储形式转换成ASCII形式二进制形式494848484848100000=216+215+210+29+27+25文件使用的过程:要使用一个文件流时,必须在程序中先打开一个文件,目的是将一个文件流类与某一个磁盘文件联系起来;其后,使用文件流类提供的成员函数,将数据写入到文件中或从文件中读取数据;当不再使用该文件流时,关闭已打开的文件,将该磁盘文件与文件流类已建立的关系脱离。使用文件的方法可概括为以下几点:(1)说明一个文件流对象。它只能是类ifstream、ofstream或fstream的对象。例:

ifstreaminfile;//仅用于输入

ofstreamoutfile;//仅用于输出

fstreamiofile;//即可输入又可输出(2)使用文件流类的成员函数或者构造函数,打开一个文件。打开文件的作用是在文件流对象与要使用的文件名之间建立联系。例:infile.open(“myfile1.txt”);//文件不存在出错

outfile.open(“myfile2.txt”);//文件不存在创建一个空文件

iofile.open(“myfile2.txt”,ios::in||ios::out);

//对文件读写(3)使用提取运算符、插入运算符或成员函数对文件进行读写操作。例:

infile>>ch;outfile<<ch;(4)用完文件后,使用成员函数关闭文件。例:

infile.close();9.4.1文件的打开与关闭1.打开磁盘文件

打开文件是指在文件读写之前做必要的准备工作:为文件流对象和指定的磁盘文件建立关联,以便使文件流流向指定的磁盘文件。(2)指定文件的工作方式。

input或output、ASCII或二进制文件等。可以通过两种不同的方法实现。(1)调用文件流的成员函数open。如ofstreamoutfile;//定义ofstream类(输出文件流类)对象outfileoutfile.open(″f1.dat″,ios::out);

//使文件流与f1.dat文件建立关联调用成员函数open的一般形式为:

文件流对象

.open(磁盘文件名,输入输出方式);磁盘文件名可以包括路径:如:″c:\\new\\f1.dat″

缺省路径,默认为当前目录下的文件。(2)在定义文件流对象时指定参数

可以在定义文件流对象时指定参数,调用文件流类的构造函数来实现打开文件的功能。如:

ostreamoutfile(″f1.dat″,ios::out);

作用与open函数相同。输入输出方式是在ios类中定义的,有多种选择。关于文件打开方式,ios中定义了一个公有枚举类型,使用方法与输入输出格式控制类似:enumopen_mode{

in=0x01,

//打开文件用于输入操作(从文件读取),文件指针在文件头

out=0x02,

/*打开文件用于写入文件。如文件不存在,则建立,但指定目录必须存在,否则建立文件失败。如文件存在,未同时设

app,ate,in则文件清空*/

ate=0x04,

//打开文件用于输入/输出,文件指针在文件尾,但新数据可写到任何位置

app=0x08,

//打开文件用于输出,但从尾部添加,新数据只能添加在尾部

trunce=0x10,

//打开文件,并清空它,以建立新文件

nocreate=0x20,

//如文件存在则打开,不存在并不创建新文件

noreplace=0x40,

//如文件不存在则创建,如存在则只能设为ate及app方式

binary=0x80

//以二进制方式打开文件};1.以in方式打开的文件,只能从文件中读取数据。以out方式打开的文件,只能将数据写入文件中。单独用该方式打开文件时,若文件不存在,则产生一个空文件;若文件存在,则先删除文件的内容,使其成为一个空文件。4.ate方式不能单独使用,要与in、out或noreplace同时使用,例如,out|ate,作用是在文件打开时,将文件指针移到文件的结尾处,文件中原来的内容不变,向文件中写入的数据增加到文件中。3.app是以写方式打开文件,当文件存在时,它等同于out|ate;而当文件不存在时,它等同于out。以trunc方式打开文件时,清空文件,若文件不存在,则建立文件,(若单独使用,与out打开文件相同)。以nocreate方式打开文件时,若文件不存在时,打开文件的操作失败(打开不成功);该方式不单独使用,总是与读或写方式同时使用,但它不能与noreplace同时使用。noreplace通常用来创建一个新文件,这种方式也不单独使用,总是与写方式同时使用。若与ate或app同时使用时,也可以打开一个已存在的文件。不以binary方式打开的文件,都是文本文件,只有明确指定以binary方式打开的文件,才是二进制文件,它总是与读或写方式同时使用。说明:(1)每一个打开的文件都有一个文件指针。(2)可以用“位或”运算符“|”对输入输出方式进行组合。

ios::in|ios::nocreate//打开一个输入文件,若文件不存在返回打开失败信息

ios::in|ios::out|ios::binary

//打开一个二进制文件,可读可写(3)如果打开操作失败,open函数的返回值为0(假),如果是用调用构造函数的方式打开文件的,则流对象的值为0。打开文件完整程序为:

fstreamiofile(“myfile.txt”,ios::in|ios::out);

if(!iofile){cout<<“不能打开文件:myfile.txt”<<endl;return-1;}2.关闭磁盘文件

打开文件后,对文件进行的读或写操作做完后,应该调用文件流的成员函数来关闭相应的文件。关闭文件用成员函数close。如:

outfile.close();//将输出文件流所关联的磁盘文件关闭关闭:

解除该磁盘文件与文件流的关联,原来设置的工作方式也失效,不能再通过文件流对该文件进行输入或输出。

也可以将文件流与其他磁盘文件建立关联,通过文件流对新的文件进行输入或输出。如:

outfile.open(″f2.dat″,ios::app|ios::nocreate);

//以输出方式打开文件,写入的数据添加在文件末尾,如文件不存在返回文件不存在信息

文件流outfile与f2.dat建立关联,并指定了f2.dat的工作方式。

当一个文件流类的对象通过打开文件函数,建立起文件名与该对象间的联系后,就可以对文件进行读或写操作,而一旦关闭文件后,文件流对象与文件名之间所建立的联系就断开了,不能再对该文件进行读或写操作。如果要再次使用该文件,必须重新打开文件。9.4.2

文本文件的读写

这里讨论文本文件的顺序读写。顺序读写可用C++的提取运算符(>>)和插入运算符(<<)进行。通过下面例子可以看出,文本文件读写与标准输入输出十分相似,原因是C++将标准输入输出设备就当作文本文件来处理。

voidmain(){//例9.7复制文件。

charch;ifstreamsfile("d:\\Ex9_6\\Ex9_6.cpp");ofstreamdfile("e:\\Ex9_6.cpp");

//只能创建文件,不能建立子目录,如路径不存在则失败

if(!sfile){cout<<"不能打开源件:"<<"d:\\Ex9_6\\Ex9_6.cpp"<<endl;

return-1;}if(!dfile){cout<<"不能打开目标件:"<<"e:\\Ex9_6.cpp"<<endl;

return-1;}sfile.unsetf(ios::skipws);

//把跳过空格控制位置0,即不跳过空格,否则空格全部未复制

while(sfile>>ch)dfile<<ch;

//完成拷贝

sfile.close();dfile.close();}1.提取(“>>”)运算符在缺省情况下是跳过空白(包括空格,制表,backspace和回车等)的,必须设置关闭跳过空白(如不设置以这样复制的文件会缺少一些字符)。2,该程序能确定文件是否复制结束。流类成员函数和运算符全是返回本类型的引用,这里就是流文件对象自身,当文件结束时,返回NULL,这时不再复制,退出循环。3,复制是按字节进行的,效率很低,按字节传递开销极大,但该程序能正确复制任意类型的文件,不仅是文本文件(看作按字符),二进制文件(看作按字节)也一样可正确完成。如果是文本文件,我们可以按行进行拷贝。4,!sfile中的“

!”是重载的运算符,在状态函数中重载,当该操作出现不正常状态,返回true。(与状态函数ios::fail()等效)注意:intmain(){

//例9.8按行复制文本文件。

charfilename[256],buf[100];fstreamsfile,dfile;cout<<"输入源文件路径名:"<<endl;cin>>filename;//输入源文件名(包括路径)sfile.open(filename,ios::in);//打开一个已存在的文件

while(!sfile){ cout<<"源文件找不到,请重新输入路径名:"<<endl; sfile.clear(0);//清状态字

cin>>filename;//重新输入源文件名(包括路径) sfile.open(filename,ios::in);}cout<<"输入目标文件路径名:"<<endl;cin>>filename;

对于流,只要出错,对应流的状态标志就设置为1,此后忽略所有对此流对象的操作,必须用clear()函数清0,然后才能正常运行。

dfile.open(filename,ios::out);//只能创建文件,不能建立子目录,如路径不存在则失败

if(!dfile){cout<<"目标文件创建失败"<<endl;return-1;}

while(sfile.getline(buf,100),sfile.eof()!=1){

//按行拷贝

if(sfile.rdstate()<100)dfile<<buf<<'\n';

//流正常

else{dfile<<buf;

//流不正常

sfile.clear();

//清0

}

}

sfile.close();dfile.close();return0;}sfile.rdstate()

读取状态字【例9.9】文本式数据文件的创建与读取数据。classinventory{stringDescription;//种类

stringNo;//型号

intQuantity;//数量

doubleCost;//成本价

doubleRetail;//零售价public:inventory(string="#",string="0",int=0,double=0,double=0);

friendostream&operator<<(ostream&dist,inventory&iv);

friendistream&operator>>(istream&sour,inventory&iv);};//流类作为形式参数必须是引用inventory::inventory(stringdes,stringno,intquan,doublecost,doubleret){Description=des;No=no;Quantity=quan;Cost=cost;Retail=ret;}ostream&operator<<(ostream&dist,inventory&iv){dist<<left<<‘\t’<<iv.Description<<‘\t’<<iv.No;dist<<right<<‘\t’<<iv.Quantity<<‘\t’<<iv.Cost<<‘\t’<<iv.Retail<<endl;

returndist;}//写入文件是自动把数转为数字串后写入istream&operator>>(istream&sour,inventory&iv){sour>>iv.Description>>iv.No>>iv.Quantity>>iv.Cost>>iv.Retail;

returnsour;}//从文件读出是自动把数字串转为数读出,函数体内>>功能不变intmain(){inventorycar1("夏利2000","805637928",156,80000,105000),car2;

inventorymotor1("金城125","93612575",302,10000,13000),motor2;

ofstreamdistfile("d:\\Ex9_9.data");distfile<<car1<<motor1;

distfile.close();cout<<car1;cout<<motor1;cout<<car2;cout<<motor2;ifstreamsourfile("d:\\Ex9_9.data");//分两次打开,可避免读文件时,误改了源文件

sourfile>>car2>>motor2;

sourfile.close();cout<<car2;cout<<motor2;

return0;}

文件中的信息不是字符数据,而是字节中的二进制形式的信息,因此它又称为字节文件。用ios::binary指定为以二进制形式传送和存储。

二进制文件除了可以作为输入文件或输出文件外,还可以是既能输入又能输出的文件。1.用成员函数read和write读写二进制文件

用istream类的成员函数read和write来实现对二进制文件的读写。9.4.3

二进制文件的读写调用的方式:

a.write(p1,50);

//将字符指针p1所给出的地址开始的50个字节的内容不加转换地写到磁盘文件中

b.read(p2,30);//从b所关联的磁盘文件中读入30个字节(或遇到EOF结束),

//存放在字符指针p2所指的一段空间内两个成员函数的原型:

istream&read(char*buffer,intlen);//只能按规定读取所指定的字符数

ostream&write(constchar*buffer,intlen);const:

不允许通过指针改变其指向数据的值

字符指针buffer指向内存中一段存储空间。

len是读写的字节数。读文件时通常并不知道文件中有多少数据,为便于程序判断是否已读到文件尾,ios类中定义了一个成员函数ios::eof(),未到文件尾时返回0,到达文件尾则返回非0值。例:将一批数据以二进制形式存放在磁盘文件中。#include<fstream>usingnamespacestd;structstudent{charname[20];intnum;intage;charsex;};intmain(){studentstud[3]={″Li″,1001,18,′f′,″Fun″,1002,19,′m′,″Wang″,1004,17,′f′};ofstreamoutfile(″stud.dat″,ios::binary);if(!outfile){cerr<<″openerror!″<<endl;abort();//退出程序

}

for(inti=0;i<3;i++)outfile.write((char*)&stud[i],sizeof(stud[i]));outfile.close();return0;}outfile.write((char*)&stud[0],sizeof(stud));该方法可以输出一批数据强制转换为字符指针例:将刚才以二进制形式存放在磁盘文件中的数据读入内存并在显示器上显示。#include<fstream>usingnamespacestd;structstudent{stringname;intnum;intage;charsex;};intmain(){studentstud[3];inti;ifstreaminfile(″stud.dat″,ios::binary);if(!infile){cerr<<″openerror!″<<endl;abort();//退出程序

}

for(i=0;i<3;i++)infile.read((char*)&stud[i],sizeof(stud[i]));infile.close();

一次读入文件中的全部数据:infile.read((char*)&stud[0],sizeof(stud));for(i=0;i<3;i++){cout<<″NO.″<<i+1<<endl;cout<<″name:″<<stud[i].name<<endl;cout<<″num:″<<stud[i].num<<endl;;cout<<″age:″<<stud[i].age<<endl;cout<<″sex:″<<stud[i].sex<<endl<<endl;}return0;}运行时在显示器上显示:NO.1NO.2NO.3name:Liname:Funname:Wangnum:1001num:1001num:1004age:18age:19age:17sex:fsex:msex:f【例】将1-500之间的偶数写入文件data.dat中,再将其显示在屏幕上。#include<iostream.h>#include<stdlib.h>voidmain(){ofstreamoutfile(“data.dat”,ios::binary);if(!outfile){cout<<“can’topenfile”<<endl;exit(1);}intn;for(n=2;n<=500;n+=2)outfile.write((char*)&n,sizeof(int));outfile.close();ifstreaminfile(“data.dat”,ios::binary);if(!infile){cout<<“can’topenfile”<<endl;exit(1);}inta[250];infile.read((char*)a,sizeof(int)*250);//从data.dat中一次读取249个整数

infile.close();

for(n=0;n<249;n++)cout<<a[n]<<‘’;

}classinventory{//例9.10stringDescription;stringNo;

intQuantity;

doubleCost;

doubleRetail;public:inventory(string="#",string="0",int=0,double=0,double=0);

voidBdatafromfile(ifstream&sour);

voidBdatatofile(ofstream

&dist);//流类作为形式参数必须是引用

friendostream&operator<<(ostream&dist,inventory&iv);

friendistream&operator>>(istream&sour,inventory&iv);};inventory::inventory(stringdes,stringno,intquan,doublecost,doubleret){Description=des;No=no;Quantity=quan;Cost=cost;Retail=ret;}ostream&operator<<(ostream&dist,inventory&iv){dist<<left<<‘\t’<<iv.Description<<‘\t’<<iv.No;dist<<right<<‘\t’<<iv.Quantity<<‘\t’<<iv.Cost<<‘\t’<<iv.Retail<<endl;

returndist;}//写入文件是自动把数转为数字串后写入istream&operator>>(istream&sour,inventory&iv){sour>>iv.Description>>iv.No>>iv.Quantity>>iv.Cost>>iv.Retail;

returnsour;}//从文件读出是自动把数字串转为数读出,函数体内>>功能不变inventory::Bdatatofile(ofstream&dist){ dist.write(Description.c_str(),20);//由string类的c_str()转换为char*

dist.write(No.c_str(),10);//由string类的c_str()转换为char* dist.write((char*)&Quantity,sizeof(int));//将整型变量强制转换成字符指针

dist.write((char*)&Cost,sizeof(double)); dist.write((char*)&Retail,sizeof(double));}inventory::Bdatafromfile(ifstream&sour){intk[20]; sour.read(k,20);Description=k; sour.read(k,10);No=k; sour.read((char*)&Quantity,sizeof(int)); sour.read((char*)&Cost,sizeof(double)); sour.read((char*)&Retail,sizeof(double));}//读写过程对称,次序不能颠倒voidmain(){Inventorycar2,motor2,car1("夏利2000","805637928",156,80000,105000),motor1("金城125","93612575",302,10000,13000);ofstreamddatafile("d.data",ios::out|ios::binary);car1.Bdatatofile(ddatafile);//将对象car1中数据复制到文件

motor1.Bdatatofile(ddatafile);//从文件读取数据复制到对象motor1中

cout<<"对象car1:"<<endl; cout<<car1;cout<<"对象motor1:"<<endl; cout<<motor1;cout<<"对象car2:"<<endl; cout<<car2;cout<<"对象motor2:"<<endl; cout<<motor2;ddatafile.close();//关闭文件ifstreamsdatafile("d.data",ios::in|ios::binary);

//重新打开文件,从头读取数据

car2.Bdatafromfile(sdatafile);//从文件读取数据复制到对象car2if(sdatafile.eof()==0)cout<<"读文件成功"<<endl;cout<<"对象car2:"<<endl;cout<<car2;motor2.Bdatafromfile(sdatafile);//继续从文件读取数据复制到对象motor2if(sdatafile.eof()==0)cout<<"读文件成功"<<endl;cout<<"对象motor2:"<<endl;cout<<motor2;sdatafile.close();}二进制文件优点:

可以控制字节长度,读写数据时不会出现二义性,可靠性高。同时不知格式是无法读取的,保密性好。文件结束后,系统不会再读(见eofbit的说明),但程序不会自动停下来,所以要判断文件中是否已没有数据。如写完数据后没有关闭文件,直接开始读,则必须把文件定位指针移到文件头。如关闭文件后重新打开,文件定位指针就在文件头。9.4.4

文件的随机访问

C++把每一个文件都看成一个有序的字节流,见图9.3,每一个文件或者以文件结束符结束,或者在特定的字节号处结束。012436578…n-1…文件结束符图9-3C++把文件看作有序的n个字节的流当打开一个文件时,该文件就和某个流关联起来了。对文件进行读写实际上受到一个文件定位指针的控制。输入流的指针称为读指针,每一次提取操作将从读指针当前所指位置开始,每次提取操作自动将读指针向文件尾移动。输出流指针也称写指针,每一次插入操作将从写指针当前位置开始,每次插入操作自动将写指针向文件尾移动。在C++中可以由程序移动文件指针,从而实现文件的随机访问,即读写文件流中某一段内容。一般文本文件很难准确定位,所以随机访问多用于二进制文件。与输入输出的格式控制类似,istream类和ostream类中分别定义了若干控制文件指针的成员函数,例如:istream&istream::seekg(streampos);

//指针直接定位istream&istream::seekg(streamoff,ios::seek_dir);

//第一个参数指明移动数量,第二个参数指明移动起点longistream::tellg();

//返回当前指针位置seek_dir是ios类中说明的一个公有枚举类型:enumseek_dir{ beg=0,//文件开头

cur=1,//文件指针的当前位置

end=2//文件结尾};datafile.tellg();返回文件指针

datafile.seekg(-20L,ios::cur);表示将文件定位指针从当前位置向文件头部方向移20个字节。datafile.seekg(20L,ios::beg);表示将文件定位指针从文件头向文件尾方向移20个字节。datafile.seekg(-20L,ios::end);表示将文件定位指针从文件尾向文件头方向移20个字节。例:tellg()和seekg()通常配合使用ostream类也提供了三个成员函数管理文件定位指针:ostream&ostream::seekp(streampos);//指针直接定位ostream&ostream::seekp(streamoff,ios::seek_dir);//指针直接定位longostream::tellp();//返回当前指针位置【例9.11】使用随机访问对【例9.10】进行改造。(自学)例9.11使用随机访问对例9.10进行改造。将入口(main())程序中的文件改为输入输出文件,写完后将文件定位指针拨回文件开始处。对应商品类中两成员函数,参数类型改为fsream&

Bdatatofile(fstream&dist);Bdatafromfile(fstream&dist);主函数相关部分如下:

fstreamdatafile("d:\\Ex9_11.data",ios::in|ios::out|ios::binary);//打开输入输出文件

car1.Bdatatofile(datafile);//保存对象

motor1.Bdatatofile(datafile);datafile.seekg(50,ios::beg);//一个记录50字节

motor2.Bdatafromfile(datafile);//先重写motor2datafile.seekg(ios::beg);//指针回到开始

car2.Bdatafromfile(datafile);//后重写car2例:有5个学生的数据,要求:(1)把它们存到磁盘文件中;(2)将磁盘文件中的第1,3,5个学生数据读入程序,并显示出来;(3)将第3个学生的数据修改后存回磁盘文件中的原有位置。(4)从磁盘文件读入修改后的5个学生的数据并显示出来。需要解决3个问题:(1)由于同一磁盘文件在程序中需要频繁地进行输入和输出,因此可将文件的工作方式指定为输入输出文件:

ios::in|ios::out|ios::binary(2)正确计算好每次访问时指针的定位,即正确使用seekg或seekp函数。(3)正确进行文件中数据的重写(更新)。#include<fstream>usingnamespacestd;structstudent{intnum;charname[20];floatscore;};intmain(){studentstud[5]={1001,″Li″,85,1002,″Fun″,97.5,1004,″Wang″,54,1006,″Tan″,76.5,1010,″ling″,96};

fstreamiofile(″stud.dat″,ios::in|ios::out|ios::binary);

//用fstream类定义输入输出二进制文件流对象iofileif(!iofile){cerr<<″openerror!″<<endl;return-1;}for(inti=0;i<5;i++)//向磁盘文件输出5个学生的数据

iofile.write((char*)&stud[i],sizeof(stud[i]));studentstud1[5];//用来存放从磁盘文件读入的数据

for(i=0;i<5;i=i+2){iofile.seekg(i*sizeof(stud[i]),ios::beg);//定位于第1,3,5学生数据开头

iofile.read((char*)&stud1[i/2],sizeof(stud1[0]));

//先后读入3个学生的数据,存放在stud1[0],stud[1]和stud[2]中

cout<<stud1[i/2].num<<″″<<stud1[i/2].name<<″″<<stud1[i/2].score<<endl;//输出stud1[0],stud[1]和stud[2]各成员的值}

cout<<endl;stud[2].num=1012;//修改第3个学生(序号为2)的数据

strcpy(stud[2].name,″Wu″);stud[2].score=60;iofile.seekp(2*sizeof(stud[0]),ios::beg);//定位于第3个学生数据的开头

iofile.write((char*)&stud[2],sizeof(stud[2]));//更新第3个学生数据iofile.seekg(0,ios::beg);//重新定位于文件开头

for(i=0;i<5;i++){iofile.read((char*)&stud[i],sizeof(stud[i]));//读入5个学生的数据

cout<<stud[i].num<<″″<<stud[i].name<<″″<<stud[i].score<<endl;}iofile.close();return0;}运行结果:1001Li85(第1个学生数据)1004Wang54(第3个学生数据)1010ling96(第5个学生数据)1001Li85(输出修改后5个学生数据)1002Fun97.51012Wu60(已修改的第3个学生数据)1006Tan76.51010ling96下面程序的输出是:

intmain(){ fstreamfile1; file1.open("text.dat",ios::out|ios::in); if(!file1){cout<<"text1.datcan'topen.\n";abort();} charch[]="1a2b3c4d5e\n"; for(inti=0;i<sizeof(ch);i++) file1.put(ch[i]); charch1; for(i=1;i<sizeof(ch);i=i+2){ file1.seekg(i);file1.get(ch1);cout<<ch1; file1.seekp(i-1);file1.get(ch1);cout<<ch1; } file1.close();}//file1.put(ch[i]);将ch字符写入文件file1中a1b2c3d4e5//file1.get(ch1);从文件中提取一个字符放在ch1中9.4.5

文件与对象

在面向对象的程序设计中,信息总是放在对象的数据成员里,并最终应该保存到文件中(称把对象存入文件)。当程序开始运行时,放在对象的数据成员里的信息得到利用和修改(由文件获得数据称作由文件重构对象)。运行结束时必须把这些信息重新保存到文件中,然后关闭文件。面向对象的C++程序设计中,应该由构造函数中打开文件并创建对象;析构函数保存和关闭文件并撤销对象。对文件而言,释放资源同时包括将对象中的信息再次存入磁盘文件。在程序运行过程中,应该将信息适时保存到相应的磁盘文件中,以免数据意外丢失。【例9.13】将商店的货物,定义为一个货物数组类。数组对象动态建立,初始为2个元素,不够用时扩充一倍。用文本数据文件建立数组元素对象,要求放在构造函数中,而数据的保存和文件的关闭放在析构函数中。第一次运行时,建立空的数据文件,由键盘输入建立数组元素对象,并写入文件,程序退出时,关闭文件;下一次运行由该文件构造对象,恢复前一次做过的工作。例9.13面向对象的C++程序设计的固定框架

classinventory{stringDescription;

//商品名称

stringNo;

//货号

intQuantity;

//数量

doubleCost;

//价格

doubleRetail;

//零售public:

inventory(string="#",string="#",int=0,double=0,double=0);

friendostream&operator<<(ostream&dist,inventory&iv);

friendistream&operator>>(istream&sour,inventory&iv);

booloperator==(inventory&){returnNo==inven.No;}

//货号为关键字

booloperator<=(inventory&){returnNo<=inven.No;}};inventory::inventory(stringdes,stringno,intquan,

doublecost,doubleret){Description=des;No=no;Quantity=quan;Cost=cost;Re

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论