1、第十三章第十三章 文件文件3.1 3.1 文件概述文件概述 文件概念文件概念 所谓文件就是:存储在外部介质上所谓文件就是:存储在外部介质上 的信息集合。的信息集合。 根据存储的介质不同可分为:根据存储的介质不同可分为: 磁盘文件磁盘文件、磁带文件磁带文件等。等。 根据内容的不同可分为:根据内容的不同可分为: 程序文件程序文件、数据数据文件文件等。等。 使用文件输入输出的必要性使用文件输入输出的必要性 这里主要讨论数据文件的输入输出,即这里主要讨论数据文件的输入输出,即如何将文件中的数据如何将文件中的数据“输入输入”到程序的数据到程序的数据结构中,如何将程序的数据结构中的数据结构中,如何将程序的
2、数据结构中的数据“输出输出”到文件中。到文件中。 以往的输入输出方法:以往的输入输出方法: 键盘输入键盘输入 ,屏幕输出。,屏幕输出。 这种方法不适用于数据量大的情况。这种方法不适用于数据量大的情况。 举例说明:举例说明:main() int i, a1000; for(i=0;i1000;i+) scanf(“%d”,a+i); for(i=0;i1000;i+) printf(“%5d”,ai); 缺缺 点:点:可能可能出现重出现重复输入。复输入。输出的数据输出的数据不能保存,不不能保存,不能进一步使能进一步使 用。用。采用文件输入输出可以克服这些缺点。采用文件输入输出可以克服这些缺点。m
3、ain() int i, a1000; for(i=0;i1000;i+) scanf(“%d”,a+i); for(i=0;i1000;i+) printf(“%5d”,ai);文件文件文件文件 C C文件分类文件分类 按在磁盘上存储的形式不同,可分为:按在磁盘上存储的形式不同,可分为: 文本文件:文本文件: 以以ASCIIASCII字符存放字符存放可见、可编辑、占可见、可编辑、占空间大。空间大。 二进制文件:二进制文件:以二进制形式存放以二进制形式存放不可见、不可编辑、不可见、不可编辑、占空间小。占空间小。 使用时可根据需要选择。使用时可根据需要选择。13.2 13.2 文件类型指针文件类
4、型指针 每个被使用的文件都在内存中开辟一每个被使用的文件都在内存中开辟一个区,用来存放文件的有关信息(如文件个区,用来存放文件的有关信息(如文件名、文件状态及文件当前位置等)。这些名、文件状态及文件当前位置等)。这些信息保存在一个信息保存在一个FILEFILE类型的结构体变量中类型的结构体变量中。 若若 FILE FILE * *fpfp; ; 则则fpfp就称为指向文件就称为指向文件类型的指针变量。访问文件通过文件指针类型的指针变量。访问文件通过文件指针进行。进行。 FILEFILE结构体类型是由系统定义的。结构体类型是由系统定义的。具体定义如下:具体定义如下: typedef struct
5、 short level; 缓冲区缓冲区“满满”或或“空空”的的程度程度 unsigned flags; 文件状态标志文件状态标志 char fd; 文件描述符文件描述符 unsigned char hold; 如无缓冲区不读取字符如无缓冲区不读取字符 short bsize; 缓冲区的大小缓冲区的大小 unsigned char *buffer; 缓冲区的位置缓冲区的位置 unsigned char *curp; 当前读写指针当前读写指针 unsigned istemp; 临时文件,指示器临时文件,指示器 short token; 用于有效性检验用于有效性检验 FILE;13.3 13.3
6、文件的打开与关闭文件的打开与关闭 对文件的读写之前应对文件的读写之前应“打开打开”该文件。该文件。 使用结束后使用结束后“关闭关闭”此文件。此文件。 文件的打开文件的打开(fopen fopen 函数函数) 用用fopenfopen函数实现对文件的打开。函数实现对文件的打开。 fopenfopen函数调用的一般形式:函数调用的一般形式: FILE FILE * *fpfp; ; fp=fopen fp=fopen( (文件名,读写方式文件名,读写方式) ); 例如:例如: fp=fopenfp=fopen(“a1”, ”r”);(“a1”, ”r”); 以只读方式打开文件以只读方式打开文件a1
7、a1。fopenfopen函数返回指向函数返回指向a1a1文件的指针,文件的指针,即即fpfp是指向是指向a1a1文件的指针变量,往后文件的指针变量,往后就可以通过就可以通过fpfp访问访问a1a1文件。文件。文件读写方式:文件读写方式: “r” 按按只读只读方式打开一个方式打开一个文本文本文件文件 “w” 按按只写只写方式打开一个方式打开一个文本文本文件文件 “a” 按按追加追加方式打开一个方式打开一个文本文本文件文件 “rb” 按按只读只读方式打开一个方式打开一个二进制二进制文文件件 “wb” 按按只写只写方式打开一个方式打开一个二进制二进制文文件件 “ab” 按按追加追加方式打开一个方式
8、打开一个二进制二进制文文件件 “r+” 按按读写读写方式打开一个方式打开一个文本文本文件文件“w+” 按按读写读写方式建立一个新的方式建立一个新的文本文本文件文件“a+” 按按读写读写方式打开一个方式打开一个文本文本文件文件“rb+” 按按读写读写方式打开一个方式打开一个二进制二进制文件文件“wb+” 按按读写读写方式建立一个新的方式建立一个新的二进制二进制文文 “ab+” 按按读写读写方式打开一个方式打开一个二进制二进制文件文件 说明:说明: (1 1)不能用)不能用”r”r”方式打开一个不存在方式打开一个不存在的文的文 件,件,”r”r”方式只读不能写。方式只读不能写。 (2 2)“w”
9、w” 方式只写不能读,具有建方式只写不能读,具有建立和立和 覆盖功能。覆盖功能。 (3 3)调用)调用fopenfopen函数时,如果返回函数时,如果返回NULLNULL则则 表示打开不成功。表示打开不成功。文件的关闭文件的关闭(fclose fclose 函数)函数)在使用完一个文件后应用在使用完一个文件后应用fclose fclose 函数关函数关 闭文件,形式为:闭文件,形式为:fclosefclose(文件指针文件指针););如:如: fclose(fpfclose(fp);); 关闭后关闭后fpfp不再指向该文件。不再指向该文件。13.4 13.4 文件的读写文件的读写 文件打开后,
10、就可以对它进行读写了。文件打开后,就可以对它进行读写了。 文本文件的读写文本文件的读写 即如何将以文本方式存放的文件输入到程即如何将以文本方式存放的文件输入到程序的数据结构中。如何将程序的数据结构中序的数据结构中。如何将程序的数据结构中的数据以文本方式输出到文件中。的数据以文本方式输出到文件中。 读对象:读对象:以文本方式存放的文件以文本方式存放的文件 写对象:写对象:以文本方式保存放的文件以文本方式保存放的文件 用于对文本文件读写的函数有用于对文本文件读写的函数有: fscanf fprintf fgetc, getc fputc, putc fgets fputs 重点介绍重点介绍 fsc
11、anf fscanf 和和 fprintffprintf。以例子说明以例子说明fscanffscanf和和fprintffprintf的使用。的使用。 例:已知文本文件例:已知文本文件f1.datf1.dat中存放有中存放有100100个学生的分数,要求读入这些数据,个学生的分数,要求读入这些数据,并按从高到低的顺序排序后输出到另一并按从高到低的顺序排序后输出到另一文件中。文件中。#include “stdio.h”void sort(int *a,int n) main() int i,a100;FILE *fp; fp=fopen(“f1.dat”, “r”); if(fp=NULL) e
12、xit(0);定义一个指向文件的指针变量打开文件,打开文件,使使fp指向文指向文件件f1.datfor(i=0;i100;i+) fscanf(fp,”%d”,a+i); fclose(fp); sort(a,100);fp=fopen(“f2.dat”, “w”);for(i=0;i100;i+) fprintf(fp,”%4d”,ai);fclose(fp);从从fp所指所指的文件中的文件中读数据读数据关闭关闭fp所所指的文件指的文件注意:文本文件的输入格式要与注意:文本文件的输入格式要与 文件中的数据格式匹配。文件中的数据格式匹配。 二进制文件的读写二进制文件的读写 即如何将以二进制方式
13、存放的文件输入即如何将以二进制方式存放的文件输入到程序的数据结构中。到程序的数据结构中。 如何将程序的数据结构中的数据以二进如何将程序的数据结构中的数据以二进制方式输出到文件中。制方式输出到文件中。读对象:读对象:以二进制方式存放存放的文件以二进制方式存放存放的文件写对象:写对象:以二进制方式保存放的文件以二进制方式保存放的文件 读写函数:读写函数: fread fwrite getw putw例:将前例中的排序结果改用二进制方式例:将前例中的排序结果改用二进制方式输出到文件输出到文件f3.datf3.dat中。中。#include “stdio.h”void sort (int *a,int
14、 n) main() int i,a100; FILE *fp; fp= f open(“f1.dat”, “r”); if(fp=NULL) exit(0);for(i=0;i100;i+) fscanf(fp,”%d”,a+i);fclose1(fp);sort(a,100);fp=fopen(“f3.dat”, “wb”);fwrite(a, sizeof(int), 100, fp );fclose(fp);数据的开始地址数据的每一项的长度数 据 的项数文件的指针如果要将二进制文件如果要将二进制文件f3.datf3.dat读到数组中,则有:读到数组中,则有: #include “std
15、io.h”main() int a100;FILE *fp;fp=fopen(“f3.dat”,“rb”);if(fp=NULL) exit(0);fread(a,sizeof(int),100,fp);fclose(fp); 13.5 13.5 文件的定位文件的定位 文件中有一个位置指针,指向当前读写文件中有一个位置指针,指向当前读写位置。如果顺序读写一个文件,每次读写完位置。如果顺序读写一个文件,每次读写完一个字符后,该位置指针自动指向下一个字一个字符后,该位置指针自动指向下一个字符位置。如果想改变这样的规律,强制使位符位置。如果想改变这样的规律,强制使位置指针指向指定位置,可以用有关函数
16、。置指针指向指定位置,可以用有关函数。 rewindrewind函数函数 rewindrewind函数的作用是使位置指针重返函数的作用是使位置指针重返回文件的开头。回文件的开头。 例:例:对文本文件对文本文件f1.datf1.dat中的中的100100个分数个分数求超过平均分的人数。求超过平均分的人数。#include “stdio.h”main() int i,a,n=0; float aver=0;FILE *fp;fp=fopen(“f1.dat”,“r”);for(i=0;i100;i+) fscanf(fp,”%d”,&a); aver+=a;aver/=100;rewind(fp)
17、;for(i=0;iaver) n+; fclose(fp); printf(“n %d”,n); fseekfseek函数和随机读写函数和随机读写 使用使用fseekfseek函数可以将位置指针指向所需函数可以将位置指针指向所需的位置。的位置。 fseekfseek函数调用的一般形式:函数调用的一般形式: fseekfseek(文件指针,位移量,参考点)(文件指针,位移量,参考点); ;以起始点为基以起始点为基准,向前移动准,向前移动的字节数的字节数0 0 或或 SEEK_SET SEEK_SET 文件开始文件开始1 1 或或 SEEK_CUR SEEK_CUR 当前位置当前位置2 2 或或
18、 SEEK_END SEEK_END 文件末尾文件末尾例:例:如果如果fpfp是指向一个存放是指向一个存放100100个整数的二进个整数的二进制文件,要读取第制文件,要读取第5050个数到变量个数到变量n n时:时: fseek(fp,sizeof(int)*(50-1),SEEK_SET); fread(&n, sizeof(int), 1,fp);例:例:如果如果fpfp是指向一个存放是指向一个存放100100个整数的文个整数的文本文件,并已知每个数按本文件,并已知每个数按3 3位数字的定长格式位数字的定长格式存放,要读取第存放,要读取第5050个数到变量个数到变量n n时:时: fsee
19、k ( fp, 3*(50-1), SEEK_SE ); fscanf ( fp, ”%3d”, &n);若要从当前位置跳过若要从当前位置跳过1010个数后读取一个数:个数后读取一个数: fseek ( fp, 3*10, SEEK_CUR ); fscanf ( fp, ”%3d”, &n); 例:已知文本文件例:已知文本文件f5.datf5.dat中存放有中存放有100100个学个学生的学号、姓名和考试成绩;要求从键盘输入生的学号、姓名和考试成绩;要求从键盘输入任一学号,检索出相应学生的数据。任一学号,检索出相应学生的数据。说明:说明:(1 1)文件)文件f5.datf5.dat中每行为一
20、个学生的数据,按定中每行为一个学生的数据,按定长格式存放,依次为:学号(整数,占长格式存放,依次为:学号(整数,占5 5格)、姓名格)、姓名(占(占1010格)、成绩(整数,占格)、成绩(整数,占4 4格)。格)。(2 2)按学号从小到大的顺序连号存放,起始学号)按学号从小到大的顺序连号存放,起始学号为为10011001。#include “stdio.h”typedef struct int num; char name20; int score; STU;main() int no; STU st; FILE *fp;fp=fopen ( “f5.dat” , ”r” );scanf (
21、“%d” , &no );fseek ( fp, (no-1001)*19, 0 );fscanf ( fp, ”%5d%10s%4d” ,&st.num ,st.name, &st.score );printf (“n%5d%10s%4d” , st.num , st.name, st.score ) ;fclose ( fp ) ; 1001 LiLi 901002 WangPing 1001003 HuHeng 75 如果是二进制如果是二进制文文 件呢?件呢?上例的检索方法称为上例的检索方法称为“定位检索定位检索”。如果是非定长格式,则需要用如果是非定长格式,则需要用“遍历检索遍历检索”
22、。 while ( !feof ( fp ) ) fscanf ( fp, ”%d%s%d”, &st.num, st.name, &st.score ); if ( st.num=no ) printf ( “n%5d%10s%4d”, st.num, st.name, st.score ); break; 速度慢,但不受限制速度慢,但不受限制 综合例:综合例: 已知文本文件已知文本文件f1.datf1.dat中存放有武汉市所有中存放有武汉市所有公民的有关性别和年龄的数据,请编写程序分公民的有关性别和年龄的数据,请编写程序分别找出其中别找出其中1010名男寿星和名男寿星和1010名女寿星,并
23、将名女寿星,并将2020名寿星的数据以文本文件的方式存入到文件名寿星的数据以文本文件的方式存入到文件f2.datf2.dat中(先男后女)。中(先男后女)。 说明:说明: 文件文件f1.datf1.dat中每行为一个公民的数据,共有中每行为一个公民的数据,共有3 3项,依次为:姓名(不超过项,依次为:姓名(不超过1010个字符)、性别(个字符)、性别(0 0表表示男,示男,1 1表示女)和年龄(整数),项间以空格分隔表示女)和年龄(整数),项间以空格分隔。 未给出公民个数,将文件中的数据读完为止未给出公民个数,将文件中的数据读完为止。算法思想:算法思想:开辟一个存放开辟一个存放2020名寿星数
24、据的结果表名寿星数据的结果表a a(结构体数组),然后逐个读取公民(结构体数组),然后逐个读取公民数据,每读取一个就向数据,每读取一个就向a a中中“判断插入判断插入”一个,男性公民往前段插,女性公民一个,男性公民往前段插,女性公民往后段插。往后段插。读一个公民读一个公民的数据到的数据到p p读 完 否?NYWanghao 0 100Liming 0 98wudan 1 99wudan 1 99xiaofangxiaofang 1 95 1 95男性插入女性插入寿星表寿星表a a#include typedef struct char name10; int sex; int age; PEP
25、;插入函数插入函数, ,将一个公民的数据插入到寿星表将一个公民的数据插入到寿星表void insert (PEP *a, int n, PEP t ) int i,j; if ( t.age an-1.age ) return ; for ( i =0; iai.age) break; for ( j=n-1; ji; j-) aj=aj-1; 移位移位 ai=t; 插入插入main() int j; PEP p,a20; FILE *fp; fp=fopen (“f1.dat”,”r”); if(!fp) exit(0); for (j=0;j20;j+) aj.age=0; while( !feof ( fp ) ) fscanf ( fp, ”%s%d%d”, p.name, &p.sex, &p.age ); insert (a+10*p.sex,10,p); fclose(fp); fp=fopen(“f2.dat”,”w”); for(j=0;j20;j+) fprintf(fp,”n%15s%2d%5d”, aj.name, aj.sex,aj.age ); fclose ( fp );