第12章 文 件 §12.1 概述 1.文件信息的集合,如一段程序、一段数据、一副图等 第12章 文 件 §12.1 概述 1.文件信息的集合,如一段程序、一段数据、一副图等 计算机操作系统以文件形式存储信息,文件是最小的管理单位 如 源文件名.c、文件名.obj、文件名.exe 本章讲的是程序在运行中数据文件的输入输出 输入文件存在磁盘上的数据文件,程序运行中将文件的数据读入 内存相应变量、数组的存储单元 输出文件程序运行中将变量、数组 的数据以文件形式存于磁盘
简单例子,读入一个数组,求最大值. for(i=0;i<5;i++) {for(j=0;j<5;j++) printf("%d ", a[i][j]); printf("\n");} printf("max=%d\n",maxvalue(25,a)); } #include "stdio.h" int maxvalue(n,p) int n,*p; {int i,t; t=*p; for(i=1;i<n;i++) if(t<*(p+i)) t=*(p+i); return(t); } void main() {int a[5][5],i,j; for(i=0;i<5;i++) for(j=0;j<5;j++) scanf("%d",&a[i][j]); void main() {int a[5][5],i,j; FILE *fp,*fp1; fp=fopen("file1.txt","r"); for(i=0;i<5;i++) for(j=0;j<5;j++) fscanf(fp,"%d",&a[i][j]); fclose(fp); fp1=fopen("file2.txt","w"); for(i=0;i<5;i++) {for(j=0;j<5;j++) fprintf(fp1,"%d ", a[i][j]); fprintf(fp1,"\n");} fprintf(fp1,"maxvlue=%d\n",maxvalue(25,a)); fclose(fp1); }
采取数据文件的好处 (1)原始数据以文件输入保证数据的正确性,减少重复输入数据操作 (2)以文件形式输出,便于打印、存档 (3)使用数据文件内存交换,小机算大题 2.C语言文件按字节流存储,形式有文本(ASCII码)和二进制数据 文本文件字符形式存储,一个字符占一个字节 二进制文件二进制数据直接存储,存储字节数由数据的类型确定 两者比较 文本文件能用编辑软件编辑,但运算慢,一般用于原始数据文件和结果输出文件 二进制文件运算快,但文件内容不能阅读,一般作为中间结果文件
3.C语言对数据文件的处理方法 程序 数据区 文件缓冲区 磁盘 内存 文件缓冲区开设途征 (1)系统自动开设,使用之前需声明,称之缓冲文件系统 (2)用户自己开设,以数组形式定义,称之非缓冲文件系统 注 第(1)符合ANSI C标准
4.文件类型指针 在缓冲文件系统文件缓冲区通过结构变量指针实现 格式 FILE *指针变量 例 FILE *fp; 操作 定义FILE类型指针,用于指向文件缓冲区,fp又称文件柄
FILE是由struct定义的类型,在stdio.h库文件中可以查到 typedef struct { short level; /* 缓冲区使用量 */ unsigned flags; /* 文件状态标志 */ char fd; /* 文件描述符 */ short bsize; /* 缓冲区大小 */ unsigned char *buffer; /* 文件缓冲区的首地址 */ unsigned char *curp; /* 指向文件缓冲区的工作指针 */ unsigned char hold; /* 其他信息 */ unsigned istemp; short token; } FILE; 每个文件具有FILE结构体和文件缓冲区 通过fp->cup指示文件缓冲区中数据存取的位置 fp->cup存取文件缓冲区数据系统自动定位
5.使用数据文件的步骤 (1)定义文件指针变量 (2)打开或建立数据文件 (3)读、写文件数据 (4)关闭数据文件 C语言对数据文件的操作通过一系列函数实现
§12.2 文件的打开和关闭 12.2.1 文件的打开 使用函数 FILE *fopen(char *filename,char *type); 如 FILE *p; p=fopen("file1.dat","r"); 其中 char *filename:数据文件名,包括路径,缺省路径为当前目录 可以字符串常量或字符串变量 指出打开数据文件的路径和文件名 例 "d:\\user\\file1.dat“ p=fopen(" d:\\user\\file1.dat ","r"); name p=fopen(name,"r"); name经定义 char name[]="file1.dat";
char *type: 字符串,指出打开文件的方式 规定 r : 打开 只读 r+: 打开 读写 w: 创建 只写 w+: 创建 读写 a : 打开 追加 a+: 打开 读写 当打开的文件为二进制文件则*type后加b,否则为文本文件 如 rb, a+b 操作 打开文件,返回文件柄。具体步骤为: 在磁盘找指定路径下的指定文件,若无指定路径则为当前目录 在内存中分配一个FILE类型结构体的单元 在内存分配文件缓冲区单元 为FILE结构体填入相应的信息 返回FILE结构体的地址
注(1)fopen调用成功返回文件柄(指针),失败返回NULL(即0),因而一般打开文件时采用判别 if((fp=fopen("file1.dat","r"))==NULL) {printf("Cannot open this file\n");exit(0);} 其中exit(0)是库函数(stdlib.h),作用是关闭所有打开的文件,并终止程序执行。参数0程序正常结束,参数非0程序不正常结束。 (2)程序开始运行时,系统自动打开三个标准文件 标准输入 stdin 键盘 标准输出 stdout 显示屏 标准出错输出 stderr 显示屏 可以使用标准文件指针指定键盘、显示屏输入输出 如 fprintf(stdout,“%s”,a); 等价 printf(“%s”,a);
12.2.2 文件的关闭 使用函数 int fclose(FILE *文件指针); 如 FILE *fp; fp=fopen("file1.dat","r"); …… fclose(fp); 操作 关闭文件,将文件缓冲区的数据写盘并释放文件缓冲区 函数返回值为0 正常关闭,其它值关闭错误 所以 if(fclose(fp)) {printf("Cannot close this file\n"); exit(0); } 注 文件使用后应及时关闭,以免数据丢失
§12.3 文件的读写 12.3.1 fputc函数和fgetc函数 1、fputc函数 格式 int fputc(char ch,FILE *fp) 其中 ch : 输出字符 *fp : 文件指针 操作 将一个字符输出到文件 函数返回 成功:返回ch,失败:返回-1(EOF) 如 FILE *p; char c; fp=fopen("file1.dat","w"); …… fputc(c,fp); 与putchar(c)比较,输出设备不同 putchar(c); 等价 fputc(c,stdout);
例 从键盘输入10个字符,写到文件f1.txt中 分析 建立文件->键盘输入->写入文件->关闭文件 #include "stdio.h" #include "stdlib.h" void main() {int i;char ch;FILE *fp; if((fp=fopen("f1.txt","w"))==0) {printf("file open error!\n"); exit(0); } for(i=0;i<10;i++) {ch=getchar(); fputc(ch,fp); if(fclose(fp)) exit(0); }
2、fgetc函数 格式 int fgetc(FILE *fp) 其中 fp : 文件指针 操作 从文件读入一个字符, 函数返回 成功:返回所取字符 失败或遇到文件结束:返回-1(EOF) 如 FILE *p; char c; p=fopen("file1.dat","r"); …… fgetc(p); 与getchar(c)比较,输入设备不同 getchar(); 等价 fgetc(stdin);
例12-4 从键盘输入10个字符,写到文件f2.txt,再从文件读出屏幕输出 分析 建立文件->键盘输入->写入文件->关闭文件 ->打开文件->读文件->屏幕输出->关闭文件 #include "stdio.h" #include "stdlib.h" main() {int i;char ch;FILE *fp; if((fp=fopen("f2.txt","w"))==0) {printf("file open error!\n"); exit(0); } for(i=0;i<10;i++) {ch=getchar(); fputc(ch,fp); if(fclose(fp)) {printf("file close error!\n"); exit(0); } if((fp=fopen("f2.txt","r"))==NULL) {printf("file open error!\n"); exit(0); } for(i=0;i<10;i++) {ch=fgetc(fp); putchar(ch); if(fclose(fp)) {printf("file close error!\n");
注:文件打开时,指针指向文件缓冲区的首部 文件读写时,指向文件缓冲区的指针自动依次下移 即 *(fp->curp)=ch; fp->curp++; fputc(ch,fp) 等价 ch=*(fp->curp); fp->curp++; ch=fgtc(fp) 等价 不能对文件指针fp和文件缓冲区指针控制改变 fp++ fp->curp++ 不允许 文件结束符EOF(end of file)控制文件结束 EOF =-1
例 从键盘输入一行字符,写到文件f3.txt,再从文件读出屏幕输出 #include "stdio.h" #include "stdlib.h" main() {char ch;FILE *fp; if((fp=fopen("f3.txt","w"))==0) {printf("file open error!\n"); exit(0); } while((ch=getchar())!='\n') fputc(ch,fp); if(fclose(fp)) {printf("file close error!\n"); if((fp=fopen("f3.txt","r"))==NULL) {printf("file open error!\n"); exit(0); } ch=fgetc(fp); while(ch!=EOF) /*或while(ch!=-1)*/ { putchar(ch); if(fclose(fp)) {printf("file close error!\n");
3、 feof 函数 格式 int feof(FILE *fp) 其中 fp : 文件指针 操作 测试文件是否结束, 结束返回值非0,否则返回0 如 FILE *fp; char c; fp=fopen("a.txt","r"); while(!feof(fp)) {c=fgetc(fp); …… } 读出文件的所有字符 ch=fgetc(fp); while(ch!=EOF) { …… } 注 与使用EOF区别 使用EOF: 先读数,后判别 feof 函数:先判别,后读数
例12-3 将磁盘文件a.txt的内容复制到文件b.txt #include "stdio.h" #include "stdlib.h" main() {FILE *fpa,*fpb; if((fpa=fopen("a.txt","r"))==NULL) {printf("can not open file a.txt!\n"); exit(0); } if((fpb=fopen(nameo,"w"))==NULL) {printf("can not open file b.txt!\n"); while(!feof(fpi)) fputc(fgetc(fpa),fpb); fclose(fpa); fclose(fpb); 如用EOF ch=fgetc(fpa); while(ch!=EOF) {fputc(c,fpb); }
12.3.2 fputs 函数和 fgets函数 1.函数fputs( ) 格式 int fputs(char *s, FILE *fp); 其中, s要写入的字符串,可以是字符数组名、字符指针和字符串常量 fp文件指针 操作 向指定的文本文件写入一个字符串,结束符'\0'不写入文件 函数返回 执行成功,函数返回所写的最后一个字符 否则,函数返回0
2.函数fgets( ) 格式 char *fgets(char *s, int n,FILE *fp); 其中, s字符数组名或字符指针 n:指定读入的字符个数 fp:文件指针 操作 从文本文件中读取字符串 函数被调用时,最多读取n-1个字符,并将读入的字符串存入s所指向内存地址开始的n-1个连续的内存单元中。 当函数读取的字符达到指定的个数,或接收到换行符,或接收到文件结束标志EOF时,将在读取的字符后面自动添加一个'\0'字符;若有换行符,则将换行符保留(换行符在'\0'字符之前);若有EOF,则不保留 函数返回值 执行成功,返回读取的字符串; 如果失败,则返回空指针,这时,s的内容不确定
例12-5将字符串"apple", "grape", "pear" 写入到磁盘文件f12-5.txt中,然后再从该文件中读出,显示到屏幕。 int main(void) { FILE *fp; int i; char a[ ][80]={"apple", "grape", "pear"}, strout[80]=""; if((fp=fopen("f12-5.txt","w"))==NULL) {printf("File open error!\n"); exit(0);} for(i=0;i<3;i++) fputs(a[i], fp); fclose(fp); if((fp=fopen("f12-5.txt","r"))==NULL) {printf("File open error!\n"); exit(0);} i=0; while(!feof(fp)) {if( fgets(strout, strlen(a[i++])+1,fp) != NULL) puts(strout); }
12.3.3 fprintf 函数和 fscanf 函数 fprintf函数与printf函数 fscanf函数与scanf函数 作用相仿,均为格式化读写数据 不同的是前者对文件,后者对显示屏、键盘操作 格式 fprintf(文件指针,格式字符串,输出表列); fscanf(文件指针,格式字符串,输入表列); 例 fprintf(fp,"%d,%6.2f",i,t); 若 i=3,t=4.5 输到文件中 3, 4.50 例 fscanf(fp,"%d,%f",&i,&t); 从文件读入数据赋值于i,t变量
例12-2 将学生的计算机等级考试成绩,包括学号,姓名和分数保存到数据文件f.txt中. 301101 张文 91 301102 陈慧 85 301103 王卫东 76 301104 郑伟 69 301105 郭温涛 55 #include<stdio.h> #include<stdlib.h> struct stud {long num; char stname[20]; int score;}st; void main() {FILE *fp;int i=0; if((fp=fopen("f.txt","w"))==NULL) {printf("file open error!\n"); exit(0);} do{scanf("%ld%s%d",&st.num,st.stname,&st.score); if(st.num>0) {if(i>0)fprintf(fp,"\n"); fprintf(fp,"%ld %s %d",st.num,st.stname,st.score); i++;} }while(st.num>0); fclose(fp); }
例12-2 将数据文件f.txt中保存的学生的计算机等级考试成绩(包括学号、姓名和分数) 读出并显示到屏幕中。 #include<stdio.h> #include<stdlib.h> struct stud {long num; char stname[20]; int score;}st; void main() {FILE *fp; if((fp=fopen("f.txt","r"))==NULL) {printf("file open error!\n"); exit(0); } while(!feof(fp)) {fscanf(fp,"%ld%s%d", &st.num,st.stname,&st.score); printf("%ld %s %d\n",st.num,st.stname,st.score); fclose(fp);
§12.4 文本文件与二进制文件 1、 文本文件 文本文件字符形式(ASCII码)存储,一个字符占一个字节 文件的打开: FILE *fopen(char *filename,char *type); char *type:打开文本表示 规定 r : 打开 只读 r+: 打开 读写 w: 创建 只写 w+: 创建 读写 a : 打开 追加 a+: 打开 读写 文件的读写: int fgetc(FILE *p) int fputc(char ch,FILE *p) fprintf(文件指针,格式字符串,输出表列); fscanf(文件指针,格式字符串,输入表列); char *fgets(char *s,int n, FILE *p) int fputs(char *s, FILE *p) int getc (FILE *p) int putc (char c,FILE *p)
2、二进制文件 二进制文件二进制数据直接存储,存储字节数由数据的类型确定 文件的打开: FILE *fopen(char *filename,char *type); char *type:打开文本表示 规定 rb : 打开 只读 rb+: 打开 读写 wb: 创建 只写 wb+: 创建 读写 ab : 打开 追加 ab+: 打开 读写 文件的读写: int fread(void *buffer,int size,int count,FILE *fp); int fwrite(void *buffer,int size,int count,FILE *fp); int getw(FILE *p) int putw(int w,FILE *p)
§12.5 顺序文件和随机文件 顺序文件 按顺序读写文件的数据 一般文本文件为顺序文件 文件缓冲区指针指针定位: 打开文件时,指针定位文件开头 rewind函数将指针移至文件开头 格式 rewind(文件指针); #include "stdio.h" main() {FILE *fp1,*fp2; fp1=fopen("file1.c","r"); fp2=fopen("file2.c","w"); while(!feof(fp1)) putchar(getc(fp1)); rewind(fp1); while(!feof(fp1)) putc(getc(fp1),fp2); fclose(fp1);fclose(fp2); }
随机文件 随机读写文件的数据 一般文本文件为二进制文件 文件缓冲区指针定位: 打开文件时,指针定位文件开头 fseek函数将指针随机定位 fread函数读文件 fwrite函数写文件
§12.6 文件程序设计 使用数据文件的步骤 (1)定义文件指针变量 (2)打开或建立数据文件 (3)读、写文件数据 (4)关闭数据文件 其中步骤(1)(2)(4)的写法基本一致,仅步骤(3)稍有区别。
例 读指定的文本文件,显示在屏幕上,如果有大写字母,则改成小写字母再输出,并统计行数。 分析:根据回车符统计文件的行数。文件名通过键盘读入。 #include "stdio.h" #include "stdlib.h" main() {int line=1;FILE *fp; char name[10],ch; gets(name); if((fp=fopen(name,"r"))==NULL) {printf("%s file open error!\n",name); exit(0);} while(!feof(fp)) {ch=fgetc(fp); if(ch>='A'&&ch<='Z')ch+='a'-'A'; putchar(ch); if(ch=='\n') line++; } fclose(fp); printf("line=%d\n",line); }
例 把文本文件中的若干整数进行排序。 分析:把文件所有数据读到一个数组上,然后对数组排序,最后再写回文件。由于文件中数据个数未知,数组大小定义按最大情况考虑。 #include "stdio.h" #include "stdlib.h" #define Max 1000 main() {int i,j,a[Max]; FILE *fp; char name[10]; gets(name); if((fp=fopen(name,"r"))==0) {printf("%s can't open!\n",name); exit(0);} for(i=0;!feof(fp);i++) fscanf(fp,"%d",&a[i]); fclose(fp); sort(a,i); if((fp=fopen(name,"w"))==0) {printf("%s can't creat!\n",name); exit(0);} for(j=0;j<i;j++) fprintf(fp,"%d ",a[j]); fclose(fp);} void sort(int a[10],int n) {int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(a[j]<a[k]) k=j; t=a[k];a[k]=a[i];a[i]=t; }}