《第12章流与文件操作.ppt》由会员分享,可在线阅读,更多相关《第12章流与文件操作.ppt(35页珍藏版)》请在三一办公上搜索。
1、第12章流与文件操作,数据的输入输出(I/O)是程序设计中的基本操作之一。数据输入可来自于键盘、鼠标、磁盘文件等设备,而数据输出的对象可以是显示器、打印机和磁盘文件等。虽然C+允许使用C语言的输入输出函数,但也重新定义了一套旨在简化、安全和易于扩充(可编程)的I/O机制。由于C+在输入时将数据由字符序列转换为二进制的对象,在输出时由二进制的对象转换为字符序列,其结果使得数据的输入输出类似于流体的流动,故称为流式I/O技术。,12.1 理解流机制,12.1.1 流与文件,I/O操作一般就是指数据在内存与文件之间的交换。磁盘文件是最常见的一类文件,但在考虑I/O操作时,计算机系统中将常用设备(如显
2、示器、键盘、打印机等)也都视为文件。目的:可以使用同样的方法来完成对这些不同目标的操作。流就是对文件的一种抽象。文件操作多是以缓冲方式进行的。这里的缓冲是指对文件数据的一种临时存储技术。,12.1.2 理解源、汇和iostream流控制类,1.iostream类及类层次C+的几个主要的I/O控制流类定义于头文件,它们是由一组具有一定层次关系的模板类组成。ios类主要内容是以枚举方式定义了一系列与I/O有关的状态标志、工作方式等常量,还包括一些控制输入输出格式的方法。ios类中定义了一个成员作为流缓冲区指针。此外,ios类还是输入流istream和输出流ostream的虚基类。,streambu
3、f类此类主要负责流缓冲区的管理,定义了设置缓冲区、将输入流和输出流与缓冲区交换数据的方法。通常,用户不需要与此类打交道。istream类和ostream类从ios类虚拟派生,继承了ios类的成员,主要是对C+所有内置类型重载了和运算符。,iostream类从istream和ostream派生,同时继承了两个类的成员,目的是能够支持输入和输出两个方向的操作。由于采用虚拟继承方式,能够保证每个iostream对象只有一份ios的拷贝。,分别由istream类、ostream类派生的istrstream类和ostrstream类,支持对数据在内存中的格式化。分别由istream类、ostream类和
4、ostream类派生的ifstream类、ofstream类和fstream类,支持对文件流的格式化I/O操作。,2.预定义的流对象头文件中定义了3个对象,代表标准设备:cin。istream类的对象,代表标准输入设备,默认为键盘。cout。ostream类的对象,代表标准输出设备,默认为显示器。cerr。ostream类的对象,代表标准出错设备,即显示错误信息的设备,默认为显示器。源cin在程序中的角色是生产者,而汇cout扮演的是消费者。只要是C+的内置类型,都可以采用源和汇实现输入和输出。,char a100;cin a;cout x=x,a=a endl;字符数组a的定义长度是100,
5、如果输入字符超过了99个(保留一个给系统存放0)就会将数据写到数组之外,导致运行错误。因此要采用类方法(cin.getline)实现输入。,12.2 构造可流的类,如果希望能像普通内置数据一样,通过C+的流来输入或输出自定义类型(如String)的数据,可以重载输入运算符和输出运算符,使自定义类成为“可流的类”。,12.2.1 进一步探讨cout和cin对象,在C+中预定义了如下的与输入和输出有关的宏和类:stdout:代表标准输出设备(显示器)的宏;stdin:代表标准输入设备(键盘)的宏。在iostream.h中定义的两个用于实现标准输入和输出的对象:istream cin(stdin);
6、ostream cout(stdout);,由于ostream类和istream类对每个内置类型都以友员函数重载了,如:friend ostream 因此,可以利用对象cout和cin正确输出和输入所有基本类型的数据。,12.2.2 重载输出运算符,重载运算符的函数原型为:ostream,/在String类定义中增加的声明friend ostream,12.2.3 重载输入运算符,重载运算符时应遵循的语法形式:istream,/在String类定义中增加的声明friend istream,12.3 格式控制,为了实现对输入输出数据的格式控制,可以采用两大类方法,分别是使用ios类的方法和流控制
7、类中定义的操控符。,12.3.1 使用流的方法,为了反映流的当前状态,在ios类中定义了一个long类型的数据成员来记录当前的控制格式(流状态),称为格式控制标志字或流状态字,源和汇根据流状态字来控制格式。为了设置流状态字,ios类还定义了一些公开的格式控制方法,用于控制流状态,如对齐方式、占用宽度、显示精度和数制等,其设置结果会反映在流状态字中。,1.ios类的方法ios类有3个直接控制输出格式的方法:int width(int):设置显示宽度,设置后只对紧接着的一次输入有效;char fill(char):设置填充字符;int precision(int):设置有效位数或精度 这3个方法每
8、个都有一个无参数的重载形式,功能是返回目前对应的流状态值。width方法只对一次输出有效,而fill和precision方法在设置后一直有效,直到重新设置。,流的状态可以通过ios类中定义的枚举型流状态常量来表示,每个流状态常量是一个二进制位,所有常量通过按位或“|”运算组合成流状态字。见课本P186.这些流状态都是ios类的静态常量,应以“ios:常量名”的方式来表示。,为了使用中的流状态常量进行格式控制,需要借助于ios类的3个方法:long flags(long state):返回或设置当前流状态字(一个长整数);long setf(long state):设置一个或多个流状态,也就是使
9、某个流状态值为1(有一些状态如hex等可能未实现);long unsetf(long state):清除一个或多个流状态,即使某个流状态值为0。,2.ostream类的方法ostream类定义的常用的方法:ostream,3.istream类的方法istream类定义的常用方法有如下几个:int get():输入一个字符。void getline(char*,int n,char delimiter=n):从输入流中读取一行,读入的字符数由n限定,n是默认的行结束符。istream&read(char*,int n):从输入流中读取指定数目的字符送入内存缓冲区(如字符数组)。,12.3.2 使
10、用操控符,使用ios、istream和ostream的方法控制格式时,不能插入到和头文件中定义了一些与之相对应的流操控符。流操控符可分为两类,即无参数的操控符和有参数的操控符。所有无参的操控符都是内置的,可直接使用,而使用有参数的操控符时必须包含头文件。,*12.3.3 内存格式化,利用流控制技术可以给出一种更通用的方法,它使用一块内存缓冲区作为设备,将数据输入到缓冲区,或从缓冲区读入到数据。这种技术常被称为内存格式化。实现内存格式化依赖头文件中定义的类ostrstream类和istrstream类。实现内存格式化的基础是将缓冲区与流相连,构成一个代表内存流的源和汇,构造方法如下:ostrst
11、ream 对象名(char*target,int size);istrstream 对象名(char*src);见exam12_2,12.4 文件流,文件分为文本文件和二进制文件。文本文件以字节为单位每个字节对应一个ASCII字符。除了少数控制字符如回车和换行外,都是可见字符,故可用一般的文字处理器来编辑。在输入和输出过程中,系统要对内存中的数据与外存中的字符形式进行转换。例如,在输出到文件时将整数10转换成n,将n转换为连续的r和n等。,二进制文件以位为单位存放0和1组成的序列,输入和输出过程中无形式转换。例如,整数12345以文本形式存储时占5个字节,存放对应的5个字符,以二进制形式存储时
12、占4字节,与内存中的整数存储形式相同。通常,非文本型的数据都以二进制方式存储。,文件流类包括ofstream类、ifstream类和fstream类。ofstream类和ifstream类从ostream类和istream类继承了实现流控制的所有要素,又增加了用于文件操作时所特有的成员。fstream类是ofstream类和ifstream类的集合体,在同一个文件中读写数据。它们都以缓冲方式提供文件的I/O服务。,12.4.1 文件打开与关闭,1.创建文件流文件操作的第一步是将目标文件与一个流相联系,创建文件流,称为打开文件。,创建文件流通过类的构造函数来实现:ifstream(char*fi
13、lename,int mode=ios:in);/创建输入流ofstream(char*filename,int mode=ios:out);/创建输出流ifstream(char*filename,int mode);/创建文件流第一个参数是一个包括路径的文件名字符串,如“E:SRCTEST.CPP”。第二个参数是ios类中定义的访问模式(枚举常量),参见表12.3。,打开文件的缺省方式是文本方式的,因此,没有ios:text这样的常量。上述模式常量可以通过按位或运算进行组合,如:ios:in|ios:out:以可读可写方式打开;ios:out|ios:binary:以二进制读方式打开。此外
14、,文件流有一个位置指针(其它流也有),打开文件后指向固定位置(多为文件头,以追加方式打开时为文件尾)。随着数据的读出或写入,位置指针向文件尾部移动相应的字节数,以保证能够处理整个文件。创建一个文件流后,就可以使用一般的I/O方法实现文件的操作了。见exam12_4。,2.测试文件打开是否成功创建文件流后,总是要检查ofstream对象是否准备就绪,文件是否已正确附着在fout对象上。,ofstream fout(d:x.txt);if(!fout)/若操作失败,没有建立文件流,不能使用 cerr”Error:unable to write to x.txt”endl;return-1;,3.文
15、件结束测试如果最后一次读出的是结束标志则文件内容已全部读完。测试采用eof方法进行。如:if(fin.eof()4.文件流的关闭由于数据在写入文件之前存放在缓冲区里,随后的某一时刻(如关闭、清刷缓冲区或缓冲区满)才能真正写入文件。因此,文件操作结束后应及时关闭,以免引起数据丢失。如:文件流名.close();见exam12_4,12.4.2 文件的读写操作,正确建立文件流后,可以利用类的方法读写文件中的数据。常用方法都是继承自ostream类和istream类的方法,包括put()、flush()和write()等写方法,以及get()、getline和read()等读方法。见exam12_5
16、,12.4.3 二进制文件,虽然可以用二进制方式打开文件,但C+文件流库中并没有二进制操作。一种解决问题的方法是从ofstream派生新类并重载运算符。但这较复杂。另一种简单的方案是依赖于类istream和ostream的read和write方法,因为这对函数不进行文本方式的转换,能够真正建立起内存到磁盘的映像。见exam12_7:将两个点、一个浮点数和一个整数写入文件后,又重新读出并显示在屏幕上。,12.4.4 文件的随机访问,文件读写的过程总是顺序的,这是指文件流的位置指针随着对文件的读写过程的进行逐渐移向文件尾部,但在一些工作中可能需要反复挑选文件中的某些特定的记录,此时要对位置指针重新
17、定位。,读文件时的随机定位主要是从istream类中继承来的三个方法:istream&seekg(long off):将位置指针定位在文件开始处的off偏移(字节数)处;istream&seekg(long off,ios:seek_dir base):将位置指针定位在以base为基准的off偏移(字节数)处;long tellg():返回当前位置指针距离文件头的偏移量(字节数)。,写文件时的随机定位输出流重新定位对应的方法是seekp和tellp,语法形式与seekg和tellg相同。,例如,利用前面程序创建的a.txt文件,可以用下面的代码对位置指针进行定位,再读出其中的一个点数据:ifstream ifs(“d:a.txt,ios:binary);/创建文件流Point px;ifs.seekg(sizeof(double)+sizeof(Point);/跳过第一个浮点数和点ifs.read(char*)/显示点,使用ofstream类和ifstream类打开文件只能进行读或写一种操作,而用fstream类可以对文件同时进行读写操作,但在只有一种操作时使用ofstream类和ifstream类更安全。,