C++文件操作总结_第1页
C++文件操作总结_第2页
C++文件操作总结_第3页
C++文件操作总结_第4页
C++文件操作总结_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

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

文档简介

文件专题1.文件的概念1.1文件是程序设计中的一个重要概念。所谓“文件”,一般是指存储在外部介质上数据的集合。一批数据是以文件的形式存放在外部介质(如磁盘、光盘盒U盘)上。操作系统是以文件为单位对数据进行管理的,也就是说,如果想找存在外部介质上的数据,必须先按文件名找到所指定的文件,然后再从该文件中读取数据。要向外部介质上存储数据也必须先建立一个文件(以文件名标识),才能向它输出数据。外存文件包括磁盘文件、光盘文件和U盘文件等。目前使用最广泛的是磁盘文件,本专题以磁盘文件为代表,在程序中对光盘文件和U盘文件的使用方法与磁盘文件相同。1.2在C++语言中,根据文件中存放的数据的格式不同,将文件分为二进制文件和ASCII文件。ASCII文件又称文本(text)文件或字符文件,它的每一个字节放一个ASCII代码,代表一个字符。二进制文件又称内部格式文件或字节文件,是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放。用ASCII码形式输出的数据是与字符一一对应的,一个字节代表一个字符,可以直接在屏幕上显示或打印出来。这种方式使用方便,比较直观,便于阅读,便于对字符逐个进行输入输出。但一般占用内存空间较多,而且要花费转换时间(二进制形式与ASCII码的转换)。用内部格式(二进制形式)输出数值,可以节省外存空间,而且不需要转换时间,但一个字节并不对应一个字符,不能直观地显示文件中的内容。如果在程序运行过程中有些中间结果数据暂时保存在磁盘文件中,以后有需要输入到内存的,这时使用二进制文件保存是最合适的。如果为了能显示和打印以供阅读,则应按ASCII码形式输出。此时得到的是ASCII文件,它的内容可以直接显示在显示屏上观看。1.3文件和文件流 文件流是以外存文件为输入输出对象的数据流。输出文件是从内存流向外存文件的数据,输入文件流是从外存文件流向内存的数据。每一个文件流都有一个内存缓冲区与之对应。 要区分文件和文件流的概念,文件流本身不是文件,而只是以文件为输入输出对象的流。若要对磁盘文件输入输出,就必须通过文件流来实现。 在C++的I/O类库中定义了几种文件类,专门用于对磁盘文件的输入输出操作:(1)ifstream类,从istream类派生而来的,用于支持从磁盘文件的输入。(2)ofstream类,从ostream类派生而来的,用于支持向磁盘文件的输出。(3)fstream类,从iostream类派生而来的,用于支持对磁盘文件的输入输出。 要以磁盘文件为对象进行输入输出,必须定义一个文件流类的对象,通过文件流对象将数据从内存输出到磁盘文件,或者通过文件流对象从磁盘文件将数据输入到内存。 其实在用标准设备为对象的输入输出中,也是要定义流对象的,如cin、cout就是流对象,C++是通过流对象进行输入输出的。由于cin、cout已在iostream.h中事先定义,所以用户不需要自己定义。在用磁盘文件时,由于情况各异(标准输入输出设备就是键盘和显示屏,是固定的,所以可以统一定义;文件虽然都是在磁盘中的,但磁盘中有很多文件,写入或读出的是不同的文件,即文件是不同的),无法事先统一定义,必须有用户自己定义。对磁盘文件的操作是通过文件流对象实现的,文件流对象是用文件流类定义的。(可以将文件流与标准输入输出流进行比较,以便理解。)2.文件的使用2.1文件的使用方法基本相同。首先打开一个文件,然后从文件中读取数据或将数据写入到文件中。在读写数据之前,必须先要打开它。当不再使用该文件时,应关闭文件,以保存文件并释放系统资源。关闭之后的文件就不能再读写,除非再次打开。 以某一种格式的数据写入到一个文件后,以后从该文件中读取数据时,只有按写入的格式依次读取数据时,读取的数据才是正确的,否则读取的数据是不正确的。从文件中读取数据的类型是否正确,系统是无法检查的。保证从文件中依次取出与原先写入的数据类型一致是设计者的责任。 文件流作为一种特殊的流,具有自己的特点:(1)在读写之前必须先打开,读写之后应该关闭。3个文件流类都提供了打开open和关闭close函数。(2)在读取文件流时要特别关注文件尾EOF的判断。有多种方式来判断文件尾。(3)随机访问。文件读写依赖读写指针,一般情况下读写指针从头向尾逐个字符或字节移动,但指针可以按顺序移动,也可以随机移动,能随机访问文件中的任何数据,而不限于按顺序访问。2.2文件处理的一般过程 要使用一个文件,必须先打开文件。打开文件的目的是将一个文件流类对象与某一个磁盘文件联系起来;然后使用文件流类的成员函数,将数据写到文件,或从文件中读取数据。当不再读写文件时就要关闭文件,以断开磁盘文件与文件流类对象的联系。C++中使用文件的步骤可概括为4步:(1)创建文件流对象。它只能是类ifstream,ofstream或fstream的对象。例如:ifstreaminfile;ofstreamoutfile;fstreamiofile;(2)打开文件。使用文件流对象的成员函数open,或者构造函数打开一个文件,在文件流对象与磁盘文件名之间建立联系。例如:infile.open(“myfile.text”); outfile.open(“myfile.text”); 前两步可以合并为一步完成,这要求在第一步创建对象时应调用含文件名的构造函数,而不是默认构造函数。(3)读写。使用提取>>运算符、或插入<<运算符或成员函数(如get/put,read/write)对文件进行读写。例如:infile>>ch; outfile<<ch;(4)关闭文件。读写操作完成后,调用成员函数close来关闭文件。例如:infile.close(); outfile.close();2.3文件的打开与关闭2.3.1打开文件是指在文件读写之前做必要地准备工作,包括:(1)为文件流对象和指定的磁盘文件建立关联,以便使文件流流向指定的磁盘文件。(2)指定文件的工作方式,如,该文件是作为输入文件还是输出文件,是ASCII文件还是二进制文件等。以上工作可以通过两种不同的方法实现。调用文件流的成员函数open。 输入文件流ifstream、输出文件流ofstream和I/O文件流fstream分别提供了打开文件函数如下: voidifstream::open(constchar*,int=ios::in,int=filebuf::openprot); voidofstream::open(constchar*,int=ios::out,int=filebuf::openprot); voidfstream::open(constchar*,int,int=filebuf::openprot); 其中,第一形参是文件名或文件的全路径名。第二个形参指定打开文件的模式,输入文件流的默认值为ios::in,表示以输入方式打开文件,只读不写;输出文件流的默认值为ios::out,表示以输出文件方式打开文件,只写不读;I/O文件流没有默认值,就需要显式指定。第三个形参指定打开文件时的保护方式,与具体操作系统有关,一般情况下只要使用默认值filebuf::openprot即可。 以上open函数中,第二个形参指定了打开文件的操作模式,所有可能的操作模式定义在一个枚举类型之中。在调用时,可用这些枚举常量的或运算来表示多种组合模式(常用按位或运算符“|”,将所需的几种打开模式组合起来使用)。 具体模式:(需要进一步了解)以in方式打开,只能从文件中读取数据,读取get指针放在文件头位置。用ios::in方式的文件必须已经存在。如果用类ifstream来产生一个流,则隐含为输入流,不必再说明使用方式。以out方式打开的文件,只能将数据写入文件中,隐含着用ofstream对象。out经常与app、ate、trunc等配合使用。单独用out方式时,若文件不存在,则产生一个空文件;若文件存在,则先删除\清空文件已有内容。ate方式打开(已经存在的)文件,将文件指针移到文件尾,以便于添加数据到文件尾端,首次写字节将添加到尾端,但再次写就写在当前位置,受put指针限定当前位置。ate方式不能单独使用,往往要与out(或noreplace)结合使用。app方式是指写入数据总是添加到文件尾端,即便是调用seekp函数改变put指针。文件必须存在,用这种方式打开的文件只能用于输出。trunc方式,打开一个文件,如果文件已经存在,则删除其中全部数据,如果文件不存在,则建立新文件。如已指定了ios::out方式,而未指定ios::app,ios::ate,ios::in,则同时默认此方式。若单独使用,与out相同,trunc方式不能与ate,app,in方式结合。binary方式打开文件作为二进制流进行操作,而不明以binary方式打开的文件都作为文本文件。这种方式会影响eof函数的不同作用。如果磁盘文件确实是二进制文件,就应该明确以binary方式打开,否则就可能导致错误。如果磁盘文件是文本文件,也可以用binary方式打开,只是不如文本流操控方便。一些文件流类的open函数有默认的打开方式。ifstream的成员函数open默认的打开方式为读文件(in)方式,另外可与(nocreate和)binary结合。例如:ios::in|ios::binary //打开已有文件作为二进制流准备读取 以上用法如果文件不存在,打开失败。ofstream的成员函数open默认的打开方式为写(out)方式,另外其它几种方式都可能结合,只是相矛盾的不能同时结合,如ate与app,trunc与ate,app,in。例如:ios::out //ios::out|ios::binary //以二进制方式打开,准备写字节,从头开始ios::out|ios::app //打开文件,作为文本流,总是添加到尾端fstream的成员函数open没有默认打开方式,因此调用该成员函数时,必须指明打开文件的方式。例如:fstreamfile;file.open(“myfile.text”,ios::in|ios::out); //表示以I/O方式打开文本文件myfile.text在定义文件流对象时指定参数以上三个文件流类都提供了相应的构造函数来打开文件:ifstream::ifstream(constchar*,int=ios::in,int=filebuf::openprot);ofstream::ofstream(constchar*,int=ios::out,int=filebuf::openprot);fstream::fstream(constchar*,int,int=filebuf::openprot);这些构造函数的形参与各自的成员函数open完全相同。因此在说明这3种文件流类的对象时,可通过这些构造函数直接打开文件。例如: ifstreamf1(“file.dat”); //A ofstreamf2(“file1.txt”); fstreamf3(“file2.dat”,ios::in);以上3条语句调用各自的构造函数,分别以读方式、以写方式和以读方式打开相应文件。因此,A行语句的作用等同于以下两个语句: ifstreamf1; f1.open(“file.dat”); 不论以何种方式,打开后都要立刻判断打开是否成功。若打开成功,则文件流对象的值为非零值;否则其值为0。 打开文件的一般格式如下: ifstreamf1(“file.dat”); if(!f1) { cout<<”不能打开输入文件:”<<”file.dat”<<endl; return;}也可以先输入要打开的文件名,然后再打开文件:charfilename[256];cout<<”输入要打开的文件名:”;cin>>filename; (这种输入方式得到的filename是一个C风格字符串)ifstreamf2(filename);if(!f2){ cout<<”不能打开输入文件:”<<filename<<endl;return;} 如果输入文件名出错,打开文件就会失败,此时你可能希望重新输入文件名,再次尝试打开文件。注意,因前一次打开失败,该输入流对象的状态标记failbit被置位,故此如果不清除出错标记,再一次尝试打开也会失败。所以打开之前先执行clear操作。例如:charfilename[256];cout<<”输入要打开的文件名:”;cin>>filename;ifstreamf2(filename);while(!f2){ cout<<”不能打开输入文件:”<<filename<<endl;cout<<”再次输入文件名:”;cin>>filename;f2.clear();f2.open(filename);return;}2.3.2关闭文件 打开文件成功后,才能对文件进行读写操作。读写完成后,应该关闭文件。尽管在程序执行结束或在撤销文件流对象时,系统会自动关闭已打开的文件,但仍应显式关闭文件。这是因为打开一个文件时,系统要为打开的文件分配一定的资源,如缓冲区等,在关闭文件时,系统就收回了该文件所占用的相应资源。这样及时关闭文件可提高资源的利用率。另一个原因是,操作系统通常限制一个程序同时打开的文件数。 3个文件类各自提供了一个关闭文件的成员函数如下: voidifstream::close(); voidofstream::close(); voidfstream::close(); 这三个成员函数的用法完全相同。 关闭文件时,系统断开磁盘文件与文件流对象之间的联系(关联)。关闭文件后,就不能再对这文件进行读或写操作。如果要再次使用该文件,必须重新打开该文件。3.编程实践3.1文本文件的使用 文本文件是按一定字符编码标准来编写字符的。例如英文字符通常采用ASCII标准,中文字符通常采用GB-2312,GB-18030等。一个文本文件通常由多行组成,一个文本文件是行的一个序列,而每一行是一个字符序列,并以换行符结尾。读写文本文件既可以按字符读写,也可以按行读写。对于文本文件,如果读到0就到达文件尾。 在Windows系统中,可用记事本Notepad打开正常观看的文件都是文本文件。 类ifstream,ofstream和fstream并没有直接定义文件读写操作的成员函数。对文件的操作由基类ios,istream,ostream中定义的成员函数来实现。对于文本文件,文件的读写操作与标准I/O流相同,也是通过提取运算符>>和插入运算符<<,get,put和getline函数来读写。3.2二进制文件的使用 二进制文件不同于文本文件,例如,可执行程序.exe文件是二进制文件,目标文件.obj、静态库.lib、动态链接库.dll,所有的图片、视频、音频文件等,都是二进制文件。二进制文件的内容看做是字节的序列,其中每个字节可以是任何值。二进制文件中的内容可看作是内存的等值映射。例如,写入一个char值就是写入1个字节,写入一个数组chara[40]就是40字节,写入一个数组intb[20]就是写入20*4字节。 二进制文件中的内容与内存中内容相同,不做数据类型转换。注意,二进制文件与文本文件的本质区别。比如一个int值,作为二进制存储占4个字节,而作为文本存储就不能确定大小。如3就占1个字节,而-2147483648要占11个字节。因此文本存储需要分隔符,而二进制存储则不需要分隔符,但要计算相对位置,并进行随机访问。 打开二进制文件,应指明以二进制ios::binary方式打开。对二进制文件的读或写应通过文件流对象的成员函数来实现,而不能通过使用提取运算符>>和插入运算符<<来读写文件。从一个二进制文件中读取数据,可以使用文件流类的get(),read()成员函数;而向一个二进制文件写入数据,可以使用put()、write()函数。二进制操作常用函数: istream&istream::read(char*pch,intnCount); //从二进制文件中提取nCount个字节,或者到文件尾,提取的字节都放入pch所指向的内存中的一段存储空间。从文件中读取多个字节的内容,读取数量由参数nCount决定。 ostream&ostream::write(constchar*pch,intnCount); //将pch所指向内存中的nCount个字节插入到二进制输出流中。向文件写入多个字节的内容,写入数量由参数nCount决定。 intistream::gcount()const; //返回最后一次提取的字符个数。常与read函数配合使用。3.3文件的随机访问 在文件读写操作中有一个文件指针的概念。在打开一个输入文件时,系统为此文件建立一个长整数long变量(设变量名为point),它的初值为0,指向文件开头准备读取。文件中的内容可以看成是由若干个有序字节组成,依次给每一个字节从0开始顺序编号,就像一个数组一样。如果文件的当前字节长度为S,那么指针的有效范围是[0,S]。 一个输入文件流(ifstream对象)有一个get指针(获取指针),指向读取的位置。当读取第n个字节时,系统修改指针的值为point+=n。每次读取数据时,均从指针所指位置开始读取,读完后再增加point值。这样指针总是指向下一次读取数据的开始位置。当指针等于S时,就到达文件尾EOF,就不能再读。 一个输出文件流(ofstream对象)有一个put指针(放置指针),指向写入位置。该指针总是指向下一次写入的位置。每次写入后,都要增加point的值,使它指向下一个写入位置。如果该指针指向文件尾,就会添加数据。如果该指针定位不是文件尾,那么写入的数据就覆盖了原有的数据。注意,不能在中间插入数据,也不能删除已写数据。 一个输入输出文件流(fstream对象)既有一个get指针,也有一个put指针。这两个指针可以独立移动。 实际上,文件指针可以自由移动,这样就可以随机读写文件。当文件指针值从小向大方向移动,称为后移,反之称为前移。文件指针既可以按绝对地址移动(以文件开头位置作为参照点),也能按相对地址移动(以当前位置或文件尾作为参照点,再加上一个位移量)。 C++中允许从文件中的任何位置开始进行读或写数据,这种读写就被称为文件的随机访问。 简言之,为了实现随机存取,C++中为一个文件定义了两个指针:一个是获取指针,它指示下一次在文件的哪个位置上发生输入操作;另一个是放置指针,它指示下一次在文件的哪一个位置上发生输出操作。函数seekg()和seekp()就可以操作这两个指针来实现随机存取。其中,seekg()函数的作用是将文件当前的获取指针从指定的某一点开始移动某个偏移量字节数,指出下一次读取数据的位置。seekp()函数的作用是将文件的放置指针从指定的某一点开始移动某个偏移量字节数,指出下一次写入数据的位置。 文件指针的操作函数:------------------------------------------------------------------------------------------------------------- istream&seekg(streampospos); //将输入流的get指针定位到pos绝对位置 istream&seekg(streamoffoff,ios::seek_dirdir); //将输入流的get指针定位到off相对位置,相对于dir streampostellg(); //返回输入流的当前定位 ostream&seekp(streampospos); //将输出流的put指针定位到pos绝对位置 ostream&seekp(streamoffoff,ios::seek_dirdir); //将输出流的put指针定位到off相对位置,相对于dirstreampostellp(); //返回输出流的当前定位------------------------------------------------------------------------------------------------------------- 上述各函数中,streampos和streamoff都是类型long的同义词,分别表示绝对位置和相对位移量。 相对定位需要指定参考点。seek_dir是类ios中定义的一个公有枚举类型,表示了相对位移的参照点如下: enumseek_dir{beg=0; //把文件开始处作为参照点(偏移量需为+)cur=1; //把文件当前位置作为参照点(偏移量可+可-)end=2; //把文件结束处作为参照点(偏移量需为-)};在相对位移时,如果参照点为ios::beg,则将第一个形参值作为文件指针的值。若为ios::cur,则将文件指针当前值加上第一个形参值的和作为文件指针的值。若为ios::end,则将文件尾的字节编号加上第一个形参值的和作为文件指针的值。 假设按输入方式打开一个文件流对象f,移动文件指针如下: f.seekg(50,ios::cur); //当前文件指针值后移50个字节 f.seekg(-40,ios::cur); //当前文件指针值前移40个字节 f.seekg(-50,ios::end); //设文件尾的编号为5000,则指针移到4950处 f.seekg(0,ios:end); //当前文件指针移到文件尾 再随机访问时,要注意以下两点:在移动文件指针时,必须保证指针值大于等于0且小于等于文件尾字节编号,否则将导致读写数据不正确。程序中可调用函数tellg和tellp来观察当前文件指针的值。使用eof()函数判断读到文件尾,如果还要移动指针再次读写,就要先清除eof标记,调用clear()函数,然后再移动指针再读写。4.4.1文本文用件的使用/*--------------------------------------------------设文本文件data.dat中有若干实数,各个实数之间用分隔符分开。例如:2456.933.745.68899.82050求出文件中的这些实数的个数和平均数。-----------------------------------------------------*/#include<iostream>#include<fstream>usingnamespacestd;intmain(){ //建立输入文件流对象 ifstreaminfile; cout<<"请输入文件名:"<<endl; charfilename[80]; cin>>filename; //打开文件 infile.open(filename); if(!infile) { cout<<"打开文件失败"<<endl; return1; } floatval; floatsum=0; intcount=0; //输入文件内容 while(infile>>val) { sum+=val; ++count; } //关闭文件 infile.close(); cout<<"个数为:"<<count<<endl; cout<<"平均值为:"<<sum/count<<endl; return0;}4.2二进制文件的使用4.2.1/*----------------------------------------------产生一个二进制数据文件data.dat,将100之内的所有素数写入文件。------------------------------------------------*/#include<iostream>#include<fstream>usingnamespacestd;//判断某整数是否是素数boolisPrime(intn){ if(n<2) returnfalse; if(n==2||n==3) returntrue; for(inti=2;i*i<=n;++i) if(n%i==0) returnfalse; returntrue;}intmain(){ //创建输出文件流对象 ofstreamoutfile; //打开文件 outfile.open("data.dat",ios::out|ios::binary); if(!outfile) { cout<<"Thefilecan'tbeopened!"<<endl; return1; } //读写操作 intcount=0; for(inti=2;i<100;++i) { if(isPrime(i)) { //cout<<i<<endl; outfile.write((char*)&i,sizeof(i)); ++count; } } //关闭文件 outfile.close(); cout<<"write"<<count<<"integers."<<endl; return0;}4./*----------------------------------------------------------------读取data.dat中的数据-------------------------------------------------------------------*/#include<iostream>#include<fstream>usingnamespacestd;intmain(){ //创建输入文件流对象 ifstreaminfile; //打开文件 infile.open("data.dat",ios::in||ios::binary); if(!infile) { cout<<"Thefilecan'tbeopened!"<<endl; return1; } //读写操作 inti=0,ival; infile.read((char*)&ival,sizeof(int)); while(!infile.eof()) { cout<<ival<<'\t'; ++i; if(i%5==0) cout<<endl; infile.read((char*)&ival,sizeof(int)); } //关闭文件 infile.close(); return0;}4.2./*---------------------------------------------------复制文件。使用成员函数read和write来实现文件的复制。设源文件是data.dat,目的文件是revInt.dat。-----------------------------------------------------*/#include<iostream>#include<fstream>#include<string>usingnamespacestd;intmain(){ //创建输入\输出文件流对象 ifstreaminfile; ofstreamoutfile; //打开输入\输出文件 //打开输入文件 cout<<"输入源文件名:"<<endl; stringsour_file; cin>>sour_file; infile.open(sour_file.c_str(),ios::in|ios::binary); if(!infile) { cout<<"不能打开源文件!"<<endl; return1; } //打开输出文件 cout<<"输入目的文件名:"<<endl; stringdest_file;// chardest_file[30]; cin>>dest_file; outfile.open(dest_file.c_str(),ios::out|ios::binary); if(!outfile) { cout<<"不能打开目的文件!"<<endl; return1; } //读写操作 charbuff[7]; intn; while(!infile.eof()) //下面这几行代码要仔细体会理解!! { infile.read(buff,7); n=infile.gcount(); outfile.write(buff,n); } //关闭文件 infile.close(); outfile.close(); //在屏幕上显示目的文件内容(验证复制是否成功) ifstreamifile("revInt.dat"); intival,i=0; ifile.read((char*)&ival,sizeof(int)); while(!ifile.eof()) { cout<<ival<<'\t'; ++i; if(i%5==0) cout<<endl; ifile.read((char*)&ival,sizeof(int)); } ifile.close(); return0;}/*------------------------------该程序不仅可以复制二进制文件,也能复制文本文件。因为文本文件也可按二进制方式打开,将字符序列作为字节序列一样处理。在while循环中,使用函数eof()来判断是否已到达文件的末尾。由于从源文件中最后一次读取的数据可能不到(要求)每次读取的字节数目,所以使用函数gcount来获得实际读入的字节数,并按实际读的字节数写到目的文件中。--------------------------------*/4.2.4/*---------------------------------------------------------编程要求: 已有一个文件data.dat保存了100以内的25个素数,并按由小到大次序排列。实现一个函数,用于读取第index个素数。index的合理范围是[0,24],如果越界则返回-1;但因data.dat文件可能添加新的元素,因此需要根据当前元素个数动态计算。-----------------------------------------------------------*/#include<iostream>#include<fstream>usingnamespacestd;//读取第index个素数,如果index越界,返回-1intgetPrime(ifstream&f,intindex){ f.seekg(0,ios::end); //定位于文件尾 intsize=f.tellg()/sizeof(int); //计算当前素数的个数 //判断检查index是否越界 if(index<0||index>=size) return-1; f.seekg(index*sizeof(int)); //定位于第index个素数位置 inttemp=0; f.read((char*)&temp,sizeof(int)); returntemp;}intmain(){ //创建输入文件流对象 ifstreaminfile; //打开文件 infile.open("data.dat",ios::in||ios::binary); if(!infile) { cout<<"Thefilecan'tbeopened!"<<endl; return1; } //读第15到25个素数 for(inti=15;i<25;++i) { cout<<getPrime(infile,i)<<'\t'; } cout<<endl; //关闭文件 infile.close(); return0;} 函数getPrime演示了seekg函数的相对定位和绝对定位,以及如何使用tellg来计算当前素数的个数。这个函数适用于从很大文件中读取指定素数,当需要多次求很大序号的素数是,该函数就具有实用价值。比如求第50001个素数,如果不采用文件存储和随机访问,就要先计算前50000个素数,这是很大的计算负担。 随机访问通常作用于二进制文件,也可以作用于文本文件,前提是文本文件中的记录和字段能确定大小或者容易区分,这样才能计算定位。5.小项目5.1/*------------------------------------------------- 定义一种文件专门记录学生分数,如学号(编号)、姓名、分数等信息。其中一个学生的信息是一条记录,可以对文件进行一系列操作。---------------------------------------------------*/#include<iostream>#include<cstring>#include<list>#include<fstream>#include<cstdlib>usingnamespacestd;//定义StudentScore类-----------------------------------------------classStudentScore{public: //构造函数 StudentScore():num(0),score(0.0) { num=NULL; } StudentScore(intn,char*nam,floats):num(n),score(s) { strcpy(name,nam); } //其它成员函数声明 intgetNum(); char*getName(); floatgetScore(); voidsetScore(floatnewscore); booloperator<(constStudentScore&ss); booloperator==(constStudentScore&ss);private: intnum; //编号(唯一) charname[20]; //姓名 floatscore; //分数};//定义StudentScore的成员函数intStudentScore::getNum(){ returnnum;}char*StudentScore::getName(){ returnname;}floatStudentScore::getScore(){ returnscore;}voidStudentScore::setScore(floatnewscore){ score=newscore;}boolStudentScore::operator<(constStudentScore&ss){ returnnum<ss.num;}boolStudentScore::operator==(constStudentScore&ss){ returnnum==ss.num;}//重载运算符<<,方便StudentScore对象的输出ostream&operator<<(ostream&os,StudentScore&ss){ os<<ss.getNum()<<'\t'<<ss.getName()<<'\t'<<ss.getScore()<<endl; returnos;}//定义StudentS类结束----------------------------------------------------------//用typedef定义一个容器类型typedeflist<StudentScore>LS;//定义列表LS类型的打印函数voidprint(LS&ls){ cout<<"编号\t"<<"姓名\t"<<"成绩\n"; cout<<"------------------------------\n"; for(LS::iteratorit=ls.begin();it!=ls.end();++it) cout<<(*it);}//定义CourseScore类--------------------------------------------------------------classCourseScore{public: //构造函数 CourseScore(); //析构函数 ~CourseScore(); //判断文件是否打开 intisOpen(); //返回文件名 constchar*getFileName()const; //返回一个记录/对象的字节大小 intgetObjBytes()const; //返回当前文件中记录/对象的个数 intgetSize(); //用列表ls中的一组对象更新整个文件 voidsave(LS&ls); //将整个文件中的所有记录加载到列表ls中 voidloadAll(LS&ls); //将对象ss更新或添加到文件中 boolupdateApp(StudentScore&ss); //按指定编号num加载到对象并返回 StudentScore*loadObj(intnum); //按指定编号num删除文件中的记录 booldeleteNum(intnum);private: char*filename; //文件名 fstreamfs; //文件流对象 //将对象写入到文件 voidwriteObj(StudentScore&ss);};CourseScore::CourseScore(){ filename="scores2.dat"; fs.open(filename,ios::in|ios::out|ios::binary);}CourseScore::~CourseScore(){ fs.close();}voidCourseScore::writeObj(StudentScore&ss){ intnum=ss.getNum(); fs.write((char*)&num,sizeof(num)); charname[20]; strcpy(name,ss.getName()); fs.write(name,20); floatscore=ss.getScore(); fs.write((char*)&score,sizeof(score));}intCourseScore::isOpen(){ returnfs.is_open();}constchar*CourseScore::getFileName()const{ returnfilename;}intCourseScore::getObjBytes()const{ returnsizeof(StudentScore);}intCourseScore::getSize(){ intcurpos=fs.tellg(); //先保存当前读get位置 fs.seekg(0,ios::end); //读定位于文件尾 intsize=fs.tellg()/getObjBytes(); //计算对象个数 fs.seekg(curpos); //恢复当前读定位 returnsize;}voidCourseScore::save(LS&ls){ fs.close(); //因文件已经以in|out|binary方式打开,所以先关闭文件 //trunc方式,打开一个文件,如果文件已经存在,则删除其中全部数据,如果文件不存在,则建立新文件 fs.open(filename,ios::out|ios::binary|ios::trunc); //打开文件, //该打开文件方式会自动清除文件中的原内容 fs.seekp(0); //写定位于文件头部 for(LS::iteratorit=ls.begin();it!=ls.end();++it) //将列表ls中的StudentScore对象写入到文件中 { writeObj(*it); } fs.close(); //文件更新完成后关闭文件 fs.open(filename,ios::in|ios::out|ios::binary); //文件更新完成后的恢复工作}voidCourseScore::loadAll(LS&ls) //将整个文件中的所有记录加载到列表ls中{ fs.seekg(0); //读定位到文件头 for(inti=0;i<getSize();++i) { intnum; fs.read((char*)&num,sizeof(num)); charname[20]; fs.read(name,20); floatscore; fs.read((char*)&score,sizeof(score)); ls.push_back(StudentScore(num,name,score)); //这一行要注意!!!!!!!!!!! }}boolCourseScore::updateApp(StudentScore&ss) //将对象ss更新或添加到文件中{ fs.seekg(0); //读定位于文件头 for(inti=0;i<getSize();++i) { //按ss.num查找文件中的记录 intnum; fs.read((char*)&num,sizeof(int)); //读文件指针所指文件中对象的编号 if(num==ss.getNum()) { //文件中有该对象的记录,更新即可 fs.seekp(-4,ios::cur); //写定位到当前记录头 writeObj(ss); //将对象写入到文件,更新记录信息 returntrue; } else { //当前记录不是该对象 fs.seekg(24,ios::cur); } } //如果文件中没有该对象的记录,则将该对象添加到文件尾 fs.seekp(0,ios::end); //写定位与文件尾 writeObj(ss); //将对象写入到文件,添加记录信息 returnfalse; //文件中无记录则添加并返回false} StudentScore*CourseScore::loadObj(intnum) //按指定编号num加载到对象并返回{ //寻找该编号对应的记录 fs.seekg(0); //定位于文件头 for(inti=0;i<getSize();++i) { intnum_record; fs.read((char*)&num_record,sizeof(int)); if(num_record==num) { //找到相应记录 charname[20]; fs.read(name,20); floatscore; fs.read((char*)&score,sizeof(score)); returnnewStudentScore(num_record,name,score); } else { fs.seekg(24,ios::cur); } } //没有与编号相对应的记录 returnNULL;}//按指定编号num删除文件中的记录//方法是:将文件中的所有记录读入到列表中,在列表中删除该对象,然后用列表更新整个文件,返回true//若要删除的记录在文件中不存在,则返回falseboolCourseScore::deleteNum(intnum){ //先按编号查找是否存在,若不存在,返回false StudentScore*pss=loadObj(num); if(pss==NULL) { returnfalse; } deletepss; //所要删除的记录存在 LSls; loadAll(ls); //将整个文件中的所有记录加载到列表ls中 for(LS::iteratorit=ls.begin();it!=ls.end();++it) { if(it->getNum()==num) { ls.erase(it); //删除该记录 break; } } save(ls); returntrue;}//定义CourseScore类结束----------------------------------------------------------//-------------------------------------------------------------------------------//定义各功能子函数//先提示,再输入一个int,出错再输入intgetInt(const

温馨提示

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

评论

0/150

提交评论