390 likes | 514 Views
第十章 文件 10.1 文件概述 ■ 文件概念 所谓文件就是:存储在外部介质上 的信息集合。 根据存储的介质不同可分为: 磁盘文件 、 磁带文件 等。 根据内容的不同可分为: 程序文件 、 数据 文件 等。. ■ 使用文件输入输出的必要性 这里主要讨论数据文件的输入输出,即如何将文件中的数据“输入”到程序的数据结构中,如何将程序的数据结构中的数据“输出”到文件中。 以往的输入输出方法: 键盘输入 ,屏幕输出。 这种方法不适用于数据量大的情况。 举例说明:.
E N D
第十章 文件 10.1 文件概述 ■文件概念 所谓文件就是:存储在外部介质上 的信息集合。 根据存储的介质不同可分为: 磁盘文件、磁带文件等。 根据内容的不同可分为: 程序文件、数据文件等。
■使用文件输入输出的必要性 这里主要讨论数据文件的输入输出,即如何将文件中的数据“输入”到程序的数据结构中,如何将程序的数据结构中的数据“输出”到文件中。 以往的输入输出方法: 键盘输入 ,屏幕输出。 这种方法不适用于数据量大的情况。 举例说明:
main() { int i, a[1000]; for(i=0;i<1000;i++) scanf(“%d”,a+i); ┊ for(i=0;i<1000;i++) printf(“%5d”,a[i]); } 缺 点: ①手工输入,效率低下,容易出错。 ②输出的数据不能保存,不便于进一步使 用。
采用文件输入输出可以克服这些缺点。 main() { int i, a[1000]; for(i=0;i<1000;i++) scanf(“%d”,a+i); ┊ for(i=0;i<1000;i++) printf(“%5d”,a[i]); } } 文件 文件
■C文件分类按在磁盘上存储的形式不同,可分为: 文本文件: 以ASCII字符存放—可见、可编辑、占空间大,读写效率低。 二进制文件:以二进制形式存放—可读性差、占空间小,读写效率高。使用时可根据需要选择。
10.2 文件类型指针 每个被使用的文件都在内存中开辟一个区,用来存放文件的有关信息(如文件名、文件状态及文件当前位置等)。这些信息保存在一个FILE类型的结构体变量中。 若 FILE *fp;则fp就称为指向文件类型的指针变量。访问文件通过文件指针进行。
FILE结构体类型是由系统定义的。 具体定义如下: typedef struct { short level;缓冲区“满”或“空”的程度 unsigned flags;文件状态标志 char fd;文件描述符 unsigned char hold;如无缓冲区不读取字符 short bsize;缓冲区的大小 unsigned char *buffer;缓冲区的位置 unsigned char *curp;当前读写指针 unsigned istemp;临时文件,指示器 short token;用于有效性检验 }FILE;
10.3 文件的打开与关闭 对文件的读写之前应“打开”该文件。 使用结束后“关闭”此文件。 ■文件的打开(fopen 函数) 用fopen函数实现对文件的打开。 fopen函数调用的一般形式: FILE *fp; fp=fopen(文件名,读写方式);
例如: fp=fopen(“a1.txt”, ”r”); 以只读方式打开文件a1.txt。 fopen函数返回指向a1.txt文件的指针,即fp是指向a1.txt文件的指针变量,往后就可以通过fp访问a1.txt文件。
文件读写方式: “r” 按只读方式打开一个文本文件 “w” 按只写方式打开一个文本文件 “a” 按追加方式打开一个文本文件 “rb” 按只读方式打开一个二进制文件 “wb” 按只写方式打开一个二进制文件 “ab” 按追加方式打开一个二进制文件
说明: (1)不能用”r”方式打开一个不存在的文 件,”r”方式只读不能写。 (2)“w”方式只写不能读,具有建立和 覆盖功能。 (3)调用fopen函数时,如果返回NULL则 表示打开不成功。
■文件的关闭(fclose 函数) 在使用完一个文件后应用fclose 函数关 闭文件,形式为: fclose(文件指针); 如:fclose(fp); 关闭后fp不再指向该文件。
10.4 文件的读写 文件打开后,就可以对它进行读写了。 ■文本文件的读写 即如何将以文本方式存放的文件输入到程序的数据结构中。如何将程序的数据结构中的数据以文本方式输出到文件中。 读对象:以文本方式存放的文件 写对象:以文本方式存放的文件
用于对文本文件读写的函数有: fscanf fprintf fgetc, getc fputc, putc fgets fputs 重点介绍 fscanf 和 fprintf。
以例子说明fscanf和fprintf的使用。 例:已知文本文件f1.txt中存放有100个学生的分数,要求读入这些数据,并按从高到低的顺序排序后输出到另一文件中。
#include “stdio.h” void sort(int *a,int n) { ……} main() { int i,a[100]; FILE *fp; fp=fopen(“f1.txt”, “r”); if(fp==NULL) exit(0); 定义一个指向文件的指针变量 打开文件,使fp指向文件f1.txt
for(i=0;i<100;i++) fscanf(fp,”%d”,a+i); fclose(fp); sort(a,100); fp=fopen(“f2.txt”, “w”); for(i=0;i<100;i++) fprintf(fp,”%4d”,a[i]); fclose(fp); } 从fp所指的文件中读数据 关闭fp所指的文件 注意:文本文件的输入格式要与 文件中的数据格式匹配。
■二进制文件的读写 (数据块) 即如何将以二进制方式存放的文件输入到程序的数据结构中。 如何将数据结构中的数据以二进制方式输出到文件中。 读对象:以二进制方式存放的文件 写对象:以二进制方式存放的文件 读写函数: fread fwrite getw putw
例:将前例中的排序结果改用二进制方式输出到文件f3.dat中。例:将前例中的排序结果改用二进制方式输出到文件f3.dat中。 #include “stdio.h” struct student { int num; char name[20]; int score; }; void sort (int *a,int n) { ……} main() { int i; struct student a[100]; FILE *fp; fp= f open(“f1.txt”, “r”); if(fp==NULL) exit(0);
for(i=0;i<100;i++) fscanf(fp,”%d %s %d”,&a[i].num,&a[i].name,a[i].score); fclose1(fp); sort(a,100); fp=fopen(“f3.dat”, “wb”); for(i=0;i<100;i++) fwrite(a+i, sizeof(struct student), 1, fp ); fclose(fp); 文件的指针 数据的开始地址 数据的每一项的长度 数据的项数
//将“f3.dat”数据读入内存并输出到屏幕 fp=fopen(“f3.dat”, “rb”); for(i=0;i<100;i++) { fread(a+i,sizeof(struct student),1,fp); printf(“num:%-5d name:%-10s score:% 3d”, a[i].num, a[i].name, a[i].score); } fclose(fp); }
10.5 文件的定位 文件中有一个位置指针,指向当前读写位置。如果顺序读写一个文件,每次读写完一个字符后,该位置指针自动指向下一个字符位置。如果想改变这样的规律,强制使位置指针指向指定位置,可以用有关函数。
■rewind函数 rewind函数的作用是使位置指针重返回文件的开头。 例:对文本文件f1.txt中的100个分数求超过平均分的人数。
#include “stdio.h” main() { int i,a,n=0; float aver=0; FILE *fp; fp=fopen(“f1.txt”,“r”); for(i=0;i<100;i++) { fscanf(fp,”%d”,&a); aver+=a; } aver/=100; rewind(fp); for(i=0;i<100;i++) { fscanf(fp,”%d”,&score); if(score>aver) n++; } fclose(fp); printf(“\n %d”,n); }
■fseek函数和随机读写 使用fseek函数可以将位置指针指向所需的位置。 fseek函数调用的一般形式: fseek(文件指针,位移量,参考点); 以起始点为基准,向前移动的字节数 0 或 SEEK_SET 文件开始 1 或 SEEK_CUR 当前位置 2 或 SEEK_END 文件末尾
例:如果fp是指向一个存放100个整数的二进制文件,要读取第50个数到变量n时:例:如果fp是指向一个存放100个整数的二进制文件,要读取第50个数到变量n时: fseek(fp,sizeof(int)*(50-1),SEEK_SET); fread(&n, sizeof(int), 1,fp);
例:如果fp是指向一个存放100个整数的文本文件,并已知每个数按3位数字的定长格式存放,要读取第50个数到变量n时:例:如果fp是指向一个存放100个整数的文本文件,并已知每个数按3位数字的定长格式存放,要读取第50个数到变量n时: fseek ( fp, 3*(50-1), SEEK_SET ); fscanf ( fp, ”%3d”, &n); 若要从当前位置跳过10个数后读取一个数: fseek ( fp, 3*10, SEEK_CUR ); fscanf ( fp, ”%3d”, &n);
例:已知文本文件f5.dat中存放有100个学生的学号、姓名和考试成绩;要求从键盘输入任一学号,检索出相应学生的数据。例:已知文本文件f5.dat中存放有100个学生的学号、姓名和考试成绩;要求从键盘输入任一学号,检索出相应学生的数据。 说明: (1)文件f5.dat中每行为一个学生的数据,按定长格式存放,依次为:学号(整数,占5格)、姓名(占10格)、成绩(整数,占4格)。 (2)按学号从小到大的顺序连号存放,起始学号为1001。
#include “stdio.h” typedef struct { int num; char name[20]; int score; }STU; main() { int no; STU st; FILE *fp;
fp=fopen ( “f5.dat” , ”r” ); scanf ( “%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 90 1002 WangPing 100 1003 HuHeng 75 ┊
上例的检索方法称为“定位检索”。 如果是非定长格式,则需要用“遍历检索”。 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.dat中存放有武汉市所有公民的有关性别和年龄的数据,请编写程序分别找出其中10名男寿星和10名女寿星,并将20名寿星的数据以文本文件的方式存入到文件f2.dat中(先男后女)。
说明: ①文件f1.dat中每行为一个公民的数据,共有3项,依次为:姓名(不超过10个字符)、性别(0表示男,1表示女)和年龄(整数),项间以空格分隔。 ② 未给出公民个数,将文件中的数据读完为止。
算法思想: 开辟一个存放20名寿星数据的结果表a(结构体数组),然后逐个读取公民数据,每读取一个就向a中“判断插入”一个,男性公民往前段插,女性公民往后段插。
寿星表a 男性插入 Wanghao 0 100 Liming 0 98 ┊ wudan 1 99 xiaofang 1 95 ┊ 读一个公民的数据到p 女性插入 读完否? N Y
#include <stdio.h> typedef struct { char name[10]; int sex; int age; } PEP;
插入函数,将一个公民的数据插入到寿星表void insert (PEP *a, int n, PEP t ){ int i,j; if ( t.age < a[n-1].age ) return ; for ( i =0; i<n; i++) if (t.age>a[i].age) break; for ( j=n-1; j>i; j--) a[j]=a[j-1]; 移位a[i]=t;插入}
main() { int j; PEP p,a[20]; FILE *fp; fp=fopen (“f1.dat”,”r”); if(!fp) exit(0); for (j=0;j<20;j++) a[j].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;j<20;j++) fprintf(fp,”\n%15s%2d%5d”, a[j].name, a[j].sex,a[j].age ); fclose ( fp ); }