*,*,单击此处编辑母版标题样式,*,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,第八章 文件,C文件概述,文件类型指针,文件的翻开和关闭,文件的读写,文件的定位,出错的检测,文件的输入输出小结,一C文件概述,C文件概述,文件(file)是程序设计中一个重要的概念。,所谓“文件一般指存储在外部介质上数据的集合。,一批数据是以文件的形式存放在外部介质(如磁盘)上的。,操作系统是以文件为单位对数据进行管理的,也就是说,如果想找存在外部介质上的数据,必须先按文件名找到所指定的文件,然后再从该文件中读取数据。要向外部介质上存储数据也必须先建立一个文件(以文件名标识),才能向它输出数据。,以前各章中所用到的输入和输出,都是以终端为对象的,即从终端键盘输入数据,运行结果输出到终端上。,从操作系统的角度看,每一个与主机相联的输入输出设备都看作是一个文件。例如,终端键盘是输入文件,显示屏和打印机是输出文件。,C文件概述,在程序运行时,常常需要将一些数据(运行的最终结果或中间数据)输出到磁盘上存放起来,以后需要时再从磁盘中输入到计算机内存。这就要用到磁盘文件。,C语言把文件看作是一个字符(字节)的序列,即由一个一个字符(字节)的数据顺序组成。,根据数据的组织形式,可分为ASCII文件和二进制文件。,ASCII文件又称文本(text)文件,它的每一个字节放一个ASCII代码,代表一个字符。,二进制文件是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放。,如果有一个整数10000,在内存中占4个字节,如果按ASCII码形式输出,那么占5个字节,而按二进制形式输出,在磁盘上只占4个字节。用ASCII码形式输出与字符一一对应,一个字节代表一个字符,一个字节代表一个字符,因而便于对字符进行逐个处理,也便于输出字符。但一般占存储空间较多,而且要花费转换时间(二进制形式与ASCII码间的转换)。用二进制形式输出数值,可以节省外存空间和转换时间,但一个字节并不对应一个字符,不能直接输出字符形式。一般中间结果数据需要暂时保存在外存上以后又需要输入到内存的,常用二进制文件保存。,C文件概述,由前所述,一个C文件是一个字节流或二进制流。它把数据看作是一连串的字符(字节),而不考虑记录的界限。换句话说,C语言中文件并不是由记录(record)组成的。在C语言中对文件的存取是以字符(字节)为单位的。输入输出的数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制。,也就是说,在输出时不会自动增加回车换行符以作为记录结束的标志,输入时不以回车换行符作为记录的间隔(事实上C文件并不由记录构成)。我们把这种文件称为流式文件。C语言允许对文件存取一个字符,这就增加了处理的灵活性。,在C语言中,没有输入输出语句,对文件的读写都是用库函数来实现的。ANSI规定了标准输入输出函数,用它们对文件进行读写。,C文件概述,有两种对文件的处理方法:,一种叫“缓冲文件系统;,一种叫“非缓冲文件系统。,所谓缓冲文件系统是指系统自动地在内存区为每一个正在使用的文件名开辟一个缓冲区。从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。如果从磁盘向内存读入数据,那么一次从磁盘文件将一批数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量)。如以下图所示。缓冲区的大小由各个具体的C版本确定,一般为512字节。,C文件概述,所谓“非缓冲文件系统是指系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。,在UNIX系统下,用缓冲文件系统来处理文本文件,用非缓冲文件系统处理二进制文件。,用缓冲文件系统进行的输入输出又称为高级(或高层)磁盘输入输出(高层I/O)。,用非缓冲文件系统进行的输入输出又称为低级(低层)输入输出系统。,ANSI C标准决定不采用非缓冲文件系统,而只采用缓冲文件系统。即既用缓冲文件系统处理文本文件,也用它来处理二进制文件。也就是将缓冲文件系统扩充为可以处理二进制文件。,本章只介绍ANSI C规定的文件系统以及对它的读写。,二文件类型指针,文件类型指针,缓冲文件系统中,关键的概念是“文件指针。每个被使用的文件都在内存中开辟一个区,用来存放文件的有关信息(如文件的名字、文件状态及文件当前位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统定义的,取名为FILE。Turbo C在stdio.h文件中有以下的文件类型声明:,typedefstruct,short level;/*缓冲区“满或“空的程度*/,unsigned flags;/*文件状态标志*/,char fd;/*文件描述符*/,unsigned char hold;/*如无缓冲区不读取字符*/,short bsize;/*缓冲区的大小*/,unsigned char*baffer;/*数据缓冲区的位置*/,unsigned ar*curp;/*指针,当前的指向*/,unsigned istemp;/*临时文件,指示器*/,short token;/*用于有效性检查*/,FILE;,文件类型指针,可以定义文件型指针变量。如:,FILEfp;,如果有n个文件,一般应设n个指针变量(指向FILE类型结构体的指针变量),使它们分别指向n个文件(确切地说指向存放该文件信息的结构体变量),以实现对文件的访问。,三文件的翻开与关闭,文件翻开fopen函数,对文件读写之前应该“翻开该文件,在使用结束之后应关闭该文件。,ANSI C规定了标准输入输出函数库,用fopen()函数来实现翻开文件。,fopen函数的调用方式通常为,FILE*fp;,fp=fopen(文件名,使用文件方式);,例如:,fp=fopen(a1,r);,可以看出,在翻开一个文件时,通知给编译系统以下3个信息:需要翻开的文件名,也就是准备访问的文件的名字。,使用文件的方式(“读还是“写等)。,让哪一个指针变量指向被翻开的文件。,文件翻开fopen函数,文件使用方式参见书上P333表13-1。,说明:,1用“r方式:只读方式,翻开的文件只能用于向计算机输入而不能用作向该文件输出数据,而且该文件应该已经存在,不能用“r方式翻开一个并不存在的文件(即输入文件),否那么出错。,2用“w方式:只写方式,翻开的文件只能用于向该文件写数据(即输出文件),而不能用来向计算机输入。如果原来不存在该文件,那么在翻开时新建立一个以指定的名字命名的文件。如果原来已存在一个以该文件名命名的文件,那么在翻开时将该文件删去,然后重新建立一个新文件。,文件翻开fopen函数,3如果希望向文件末尾添加新的数据(不希望删除原有数据),那么应该用“a方式翻开。但此时该文件必须已存在,否那么将得到出错信息。翻开时,位置指针移到文件末尾。,4用“r+、“w+、“a+方式翻开的文件既可以用来输入数据,也可以用来输出数据。用“r+方式时该文件应该已经存在,以便能向计算机输入数据。用“w+方式那么新建立一个文件,先向此文件写数据,然后可以读此文件中的数据。用“a+方式翻开的文件,原来的文件不被删去,位置指针移到文件末尾,可以添加,也可以读。,文件翻开fopen函数,5如果不能实现“翻开的任务,fopen函数将会带回一个出错信息。,出错的原因可能是用“r方式翻开一个并不存在的文件;磁盘出故障;磁盘已满无法建立新文件等。此时fopen函数将带回一个空指针值NULL(NULL在stdio.h文件中已被定义为0)。,常用下面的方法翻开一个文件:,if(fp=fopen(file1,r)=NULL),printf(cannot open this filen);,即先检查翻开的操作有否出错,如果有错就在终端上输出“cannot open this file。,文件翻开fopen函数,6用以上方式可以翻开文本文件或二进制文件,这是ANSI C的规定,用同一种缓冲文件系统来处理文本文件和二进制文件。但目前使用的有些C编译系统可能不完全提供所有这些功能(例如有的只能用“r、“w、“a方式),有的C版本不用“r+、“w+、“a+,而用“rw、“wr、“ar等,请读者注意所用系统的规定。,(7)在向计算机输入文本文件时,将回车换行符转换为一个换行符,在输出时把换行符转换成为回车和换行两个字符。在用二进制文件时,不进行这种转换,在内存中的数据形式与输出到外部文件中的数据形式完全一致,一一对应。,文件翻开fopen函数,8在程序开始运行时,系统自动翻开3个标准文件:标准输入、标准输出、标准出错输出。通常这3个文件都与终端相联系。因此以前我们所用到的从终端输入或输出都不需要翻开终端文件。系统自动定义了3个文件指针stdin、stdout和stderr,分别指向终端输入、终端输出和标准出错输?也从终端输出)。如果程序中指定要从stdin所指的文件输入数据,就是指从终端键盘输入数据。,文件关闭fclose函数,在使用完一个文件后应该关闭它,以防止它再被误用。“关闭就是使文件指针变量不指向该文件,也就是文件指针变量与文件“脱钩,此后不能再通过该指针对原来与其相联系的文件进行读写操作。除非再次翻开,使该指针变量重新指向该文件。,用fclose函数关闭文件。,fclose函数调用的一般形式为,fclose(文件指针);,例如:,fclose(fp);,fclose函数也带回一个值,当顺利地执行了关闭操作,那么返回值为0;否那么返回EOF(-1)。,文件关闭fclose函数,应该养成在程序终止之前关闭所有文件的习惯,如果不关闭文件将会丧失数据。,因为,如前所述,在向文件写数据时,是先将数据输到缓冲区,待缓冲区充满后才正式输出给文件。如果当数据未充满缓冲区而程序结束运行,就会将缓冲区中的数据丧失。用fclose函数关闭文件,可以防止这个问题,它先把缓冲区中的数据输出到磁盘文件,然后才释放文件指针变量。,四文件的读写,fputc写文件函数,把一个字符写到磁盘文件上去。其一般调用形式为:,fputc(ch,fp);,其中:,ch是要输出的字符,它可以是一个字符常量,也可以是一个字符变量。,fp是文件指针变量。,fputc(ch,fp)函数的作用是将字符(ch的值)输出到fp所指向的文件中去。,fputc函数也带回一个值:如果输出成功那么返回值就是输出的字符;如果输出失败,那么返回一个EOF(-1)。EOF是在stdio.h文件中定义的符号常量,值为-1。,fputc写文件函数,在前面介绍过putchar函数,其实putchar是从fputc函数派生出来的。putchar(c)是在stdio.h文件中用预处理命令#define定义的宏:,#defineputchar(c)fputc(c,stdout),前面已表达,stdout是系统定义的文件指针变量,它与终端输出相连。,fputc(c,stdout)的作用是将c的值输出到终端。用宏putchar(c)比写fputc(c,stdout)简单一些。,从用户的角度,可以把putchar(c)看作函数而不必严格地称它为宏。,fgetc读文件函数,从指定的文件读入一个字符,该文件必须是以读或读写方式翻开的。,fgetc函数的调用形式为,ch=fgetc(fp);,fp为文件型指针变量,ch为字符变量。fgetc函数带回一个字符,赋给ch。如果在执行fgetc函数读字符时遇到文件结束符,函数返回一个文件结束标志EOF(-1)。,如果想从一个磁盘文件顺序读入字符并在屏幕上显示出来,可以:,ch=fgetc(fp);,while(ch!=EOF),putchar(ch);,ch=fgetc(fp);,feof()判断文件是否结束函数,注意:EOF不是可输出字符,因此不能在屏幕上显示。由于字符的ASCII码不可能出现-1,因此EOF定义为-1是适宜的。当读入的字符值等于-1(即EOF)时,表示读入的已不是正常的字符而是文件结束符。,但以上只适用于读文本文件的情况。现在ANSI C已允许用缓冲文件系统处理二进