文件是指存储在外存中的数据集合。
输入到计算机内存中的数据是不能长期保存的,机器一旦断电,内存中的信息全部丢失。
一、文件类型概述
C语言中把所有的外部设备都按文件处理,如键盘属于标准输入文件,显示器属于标准输出文件。在C语言中,文件被看成一个字符(字节)的序列,因此称为流式文件。
1.ASCII文件和二进制文件
在C语言中文件有两种存储方式:其一,数据以字符格式编码,即写入文件的一切数据都被看作是字符,因此文件是由一个一个字符组成的。在文件存储区中,每一个字节存放一个ASCII码,按这种方式编码形成的文件称为“ASCII”文件或“字符流”文件(文本文件);其二,存储在文件中的数据一律以内存中的存储形式(即二进制编码形式)存储,一个字节不对应一个字符,按这种编码方式形成的文件称为“二进制”文件。
数据若以二进制格式编码,存储开销小,而且数据从内存写入磁盘的时候不必转换,若以字符格式编码,除了存储开销大外,内、外存交换数据时需要转换内码,但它的好处是文件可以直接阅读,即在DOS下用Type命令就可以阅读文件内容,而二进制文件是不可读的。
文本文件由一个个的字符组成,并以EOF(-1)作为文件结束标志。
fread和fwrite的区别2.缓冲型文件系统和非缓冲型文件系统
这是指C语言处理文件的方式。为了提高程序的执行效率,减少程序对磁盘的读写速度,在操作系统的管理下,C语言为每一个已经打开的文件在内存中开辟出一块“缓存区”,当由内存向磁盘写数据时,先将数据转存缓存区,待缓存区装满以后一并写入磁盘。同样,从磁盘像内存中读入数据时,也是先将读出的数据转存缓存区,待装满以后才从程序一步一步的读入处理。如果一个程序同时打开多个文件,系统会自动在内存中为各个文件开辟各自的缓存区并编写相应的号码,使文件的操作互不干扰,这种文件处理方式称为“缓冲型文件系统”。
非缓冲型文件处理系统是指系统不自动为已打开的文件开辟内存中的缓存区,而是由用户自己在程序中设定,这种处理方式由于可移植性差,ANSI标准已经不再采用。
3.文件类型和文件指针
在C编译系统,文件类型是编译系统预先定义的一种结构类型,取名为FILE,用于存储文件的相关信息。其结构组成为:
typedef struct
{
short level;/*缓存区满或空的标志*/
unsigned mode;/*使用状态标志*/
int fd;/*文件号*/
short buffsize;/*缓存区大小*/
char*buff;/*缓存区位置*/
char*curp;/*文件指针当前的指向*/
}FILE;
FILE类型是系统预定义的结构,其中包含的成员数不固定,随编译系统版本的不同而不同。
FILE类型是在头文件stdio.h中定义的。
FILE结构体类型中有一个读写位置指针成员,用来指向文件的当前读写字节。每读写一次,读写位置指针会向后移动。
文件指针是文件的读写的位置标志。要正确读写文件就必须知道文件的各种信息:比如文件的名字、允许使用的方式、缓存区的位置和空闲字节数、文件当前的读写位置等。
指向文件的指针变量称为“文件指针”,它是用FILE类型来定义的,其形式为:
FILE*指针变量名;
二、文件的打开与关闭
1.文件的打开
打开文件可以调用函数fopen来实现,其形式为:
fopen("文件名","使用方式");
其中的文件名为字符串表达式,允许带目录路径和扩展名;使用方式也为字符串表达式,它指定文件允许的读写方式;使用fopen函数打开文件后,会返回文件的存储首地址。若函数调用失败,则返回NULL。
文件使用方式
文件类型使用方式含义备注指定文件不存在时指定文件存在时
ASCII文
"w"文件只允许写,不允许读新文件建立新文件文件中原有内容丢失"r"文件只允许读,不允许写老文件出错,函数返回NULL正常打开,函数fopen返回文件指针"a"只允许在文件尾部写老文件建立新文件数据追加到文件末尾
"w+"
文件允许读、写,但必须先写
后读
新文件建立新文件文件中原有内容丢失
"r+"文件允许读、写老文件出错,函数fopen返回NULL正常打开,函数fopen返回文件指针"a+"文件允许读,也允许在尾部写老文件建立新文件数据追加到文件末尾
二进制文
"wb"文件只允许写,不允许读新文件建立新文件文件中原有内容丢失"rb"文件只允许读,不允许写老文件出错,函数fpen返回NULL正常打开,函数open返回文件指针"ab"只允许在文件尾部写老文件建立新文件数据追加到文件末尾"wb+"
文件允许读、写,但必须先写
后读
新文件建立新文件文件中原有内容丢失"rb+"文件允许读、写老文件
出错,函数fopen函数返回
NULL
正常打开,函数fopen返回文件指针"ab+"文件允许读,也允许在尾部写老文件建立新文件数据追加到文件末尾
说明:以r、w、r+和w+方式打开文件时,系统自动将文件的读写位置指针移动到文件
的开头;而以a和a+的方式打开文件时,系统将自动将文件的读写位置指针移动到文件的
末尾。
在C程序端,终端也是文件,而且是系统内部预定义的标准文件。任何一个程序开始
运行时,系统都自动为其打开三个标准文件:标准输入、标准输出和标准出错,并分别由文
件指针stdin,stdout和stderr指向,它们都指向终端,因此编写普通从终端输入、输出的程
序都不必打开终端文件。
2.文件的关闭
文件使用完毕应及时将其关闭,取消文件的指针指向,以防止程序中的其他后续语句的
误操作造成文件内容的修改或丢失。关闭文件使用函数fclose实现,形式如下:fclose(文件指针);
调用fclose函数也将返回一个函数值,若函数调用成功就返回0,否则返回非0值。
C语言提供的ferror函数可以用来测试fclose函数调用是否成功。另外对于前面提到的
程序每次运行系统自动为其打开的三个标准文件,不需要用户程序负责关闭,系统会自动关
闭。
三、文件的读写
fputc函数和fgetc函数,每次对文件读或写一个字符。
1.fputc函数
形式:fputc(ch,fp);
功能:把字符ch(允许是字符常量或字符变量)的值写入由指针fp指向的文件中去。如果函数调用失败,函数返回EOF。符号常量EOF已在stdio.h头文件中定义为-1,这是由于字符的ASCII码不可能为-1。
2.fgetc函数
形式:ch=fgetc(fp);
功能:从指针fp当前指向的文件中读出一个字符并送入字符变量ch的存储单元中。
说明:当getc函数读入文件时遇到文件结束符,将返回EOF(-1),这表示读出的不是正常的字符,而是文件结束符。因此在C程序中,判断ASCII文件是否已经读结束,用fgetc(fp)==EOF或fgetc(fp)!=EOF即可实现有效的判断。
fputs函数和fgets函数能一次对ASCII文件读出或写入一个字符串。
3.fputs函数
形式:fputs(str,fp);
功能:把字符串str(允许是字符串常量或字符串变量)所标识的字符串(不包括结束符“\0”)写入由指针fp指向的文件中去,调用成功返回0,否则返回-1(即EOF)。
4.fgets函数
形式:fgets(str,n,fp);
功能:从文件指针fp当前指向的文件中读出n-1个字符,存入字符数组str中,并在str 数组的第n个字节中填入结束符“\0”,函数返回字符串的存储首地址str。
说明:若还未读完n-1个字符已遇换行符“\n”或文件结束符EOF,则读操作自动结束,并将读到的换行符“\n”连同结束符“\0”一起写入str数组中,因此可以通过fgets函数所然会的值是否是NULL来确定文件是否已经读结束。也就是说,读取过程结束有三种情况:(1)已经读取了n-1个字符(2)当前读取的字符是换行符(3)已读取到文件的末尾。
fwrite函数和fread函数主要用于二进制文件的读写,这两个文件能够对文件进行成批数据的读写,而且还能读写任何类型的数据。
5.fwrite函数
形式:fwrite(po,size,n,fp);
功能:将首地址为po的连续n*size个字节的内容(即n个size大小的数据块),写入指针fp所指向的二进制文件中。
6.fread函数
形式:fread(po,size,n,fp);
功能:从指针fp当前指向的二进制文件中连续读出n*size个字节的内容(即连续读出n个size字节的数据块),并存入首地址为po的内存区域中。
说明:
(1)调用fwrite函数和fread函数照样有函数返回值,若调用成功,返回的是写入或读出的数据块(size字节块)的个数,即n的值;若返回零,表示出错。
(2)判断字符文件是否已经读结束,可以根据所读取的字符是否为EOF(即-1)来确定。但是二进制数据文件中出现的数据可能是-1,因此用是否等于-1判断二进制文件是否结尾是不行的。C语言还提供了feof函数用于判断文件的读操作是否已经结束,它即适用于ASCII文件也使用于二进制文件。调用feof函数若返回1,表示文件已经读结束;返回0表示未结束。
fscanf函数和fprintf函数为格式读、写函数,对象为文件。与scanf和printf函数相比,格式输入输出函数的读写对象为终端。对文件进行格式读写必须遵循一下调用形式:fscanf(文件指针,格式说明,输入列表);
fprintf(文件指针,格式说明,输入列表);
说明:对文件进行格式化读写,使用方便、容易理解,但它们是以文本格式读写的,当执行fscanf函数读文件时,先将文件中的ASCII码转换为二进制数,然后才读入内存;反之,用fprintf函数向文件中写数据时,不仅程序的效率会降低,而且还会造成数据类型混淆。因此建议用fread函数和fwrite函数对文件读写。
四、文件的定位与出错检测
1.文件的定位文件一旦被正确打开,就会有一根指向该文件的开始处,以后对文件每执行一次读或写操作,指针就后移到下一个读写位置,为下一次读写做好准备,这种读写方式称为“顺序读写”。因此,任何时刻,只有指针指向的位置才是当前可以读写的字节。但实际应用中往往需要对文件进行随机读写,这就需要将文件指针移动到用户希望的位置处。为此,C语言提供了一下函数用于文件指针的随机移动:
形式:rewind(fp);
功能:将文件指针绝对定位至文件的开始处,函数无值返回。
2.fseek函数
形式:fseek(fp,n,tag);
说明:fp为文件指针,n为位移的字节数,必须为长整型,因此其数值的末尾必须加上字母L;tag代表位移的基点(即起始点),它既可以用数字0、1、2表示,也可以用符号名SEEK_SET、SEEK_CUP、SEEK_END表示。
功能:将文件指针移到离文件开始处的第n个字节处(tag=0),或从指针当前指向位置开始移动n个字节(tag=1),或移动到离文件尾部的前第n个字节处(tag=2)。
当位移量n>0时,指针往后移(即往文件的尾部方向移动);反之n<0,则往前移(即往文件开始的方向移动)。
注意,fseek函数仅适用于二进制文件,若用于ASCII文件,由于字符在输入输出过程中需要作内码转换,这会造成位置出错而是fseek函数调用失败。
指针位移基点tag值
值基点
0SEEK_SET文件开始
1SEEK_CUP文件指针当前指向的位置
2SEEK_END文件末尾
3.ftell函数
ftell函数能够用于测试文件指针当前的指向,是文件的随机读写能正确进行。
形式:ftell(fp);
功能:测试并返回文件指针指向的位置相对于文件开始处的位移量。返回的函数值为long型整数,如果文件打开但未开始读写,则返回0;如果返回-1L,则表示出错。
五、文件的出错检测与处理
在磁盘的输入输出操作中,可能会出现各种各样的错误,例如,磁盘介质的缺陷或磁盘驱动器为准备就绪或引用文件路径不正确都会造成文件读写的错误。为了避免出错,C语言又提供了几个用于检查、处理文件读写错误的函数。
1、ferror函数
形式:ferror(fp);
功能:测试文件操作是否有错误,若返回值为零,表示正确,否则表示出错。
说明:
(1)调用fopen函数时,ferror的函数初值自动置为零。
(2)对同一个文件,每次调用ferror函数都会产生一个新的函数值,因此对文件每执行一次读写操作以后,应该及时检查ferror的函数值是否正确,以避免数据丢失。
2.clearerr函数
形式:clearerr(fp);
功能:使由指针fp指向的文件出错标志连同文件结束标志一起复位成0.
说明:若对文件读写时出现错误,ferror函数就返回一个非零值,而且该值一直保留到对文件执行下一次读写为之。若及时调用clearerr函数就能清除出错标志,是ferror的函数返回值复位为0.
形式:exit([status]);
功能:关闭所有已经打开的文件,写出文件缓存区的所有数据,程序返回操作系统。
说明:参数status为状态值,它被传递到调用函数。按惯例,若status取零值,表示程序正常终止,否则
表示有错误而终止。若缺省将无返回值。