640 likes | 892 Views
第 7 章 文件处理程序设计. 主要内容. 如何将一个文件复制到另一个文件 文件的打开和关闭 出错检查 文件的顺序读写 如何读取某个客户的银行帐号信息 文件定位 文件的随机读写. 提出问题. 如何将一个文件的内容复制到另一个文件中?. 问题的分析. 前面程序处理的数据要么是直接 写在程序中 ,要么是在程序运行时由 键盘输入 ,而程序的处理结果只在 屏幕 上 显示 。当程序要处理大量数据时,这种数据处理方式就会带来诸多不便。. 问题的分析.
E N D
主要内容 • 如何将一个文件复制到另一个文件 • 文件的打开和关闭 • 出错检查 • 文件的顺序读写 • 如何读取某个客户的银行帐号信息 • 文件定位 • 文件的随机读写
提出问题 • 如何将一个文件的内容复制到另一个文件中?
问题的分析 • 前面程序处理的数据要么是直接写在程序中,要么是在程序运行时由键盘输入,而程序的处理结果只在屏幕上显示。当程序要处理大量数据时,这种数据处理方式就会带来诸多不便。
问题的分析 • 操作系统是以文件为单位对数据进行管理的,如果要找外存上的数据,必须先按文件名找到指定的文件,然后再从该文件中读取数据;如果要向外部介质上存储数据也必须先建立一个文件才能向它输出数据。 • 文件中最常见的操作就是将一个文件中的内容复制到另一个文件中去。哪么文件是什么呢?
文件的基本概念 • 所谓“文件”一般指存储在介质上的数据的集合。在C语言中,文件就是由一个一个的字符或字节的数据顺序组成的字符或字节序列,换句话说,C语言是把一个文件看作是一个有序的字节流。
C语言对文件的存取是以字节为单位的,也就意味着以字符形式进行操作。C语言对文件的存取是以字节为单位的,也就意味着以字符形式进行操作。 • 每一个文件通常以文件结束标志(end-of-file,也就是EOF)结束,
文件的分类 • 从用户的角度,文件分为: • 标准设备文件 • 普通磁盘文件 • 从文件的编码方式,文件分为: • ASCII文件 • 二进制文件
文件分类 • 从用户的角度分文件分为 • 标准设备文件 • 普通磁盘文件 • 把外部设备也看作是一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。
键盘为标准的输入文件,文件指针为stdin。从键盘上输入就意味着从标准输入文件上输入数据。如scanf、getchar函数。键盘为标准的输入文件,文件指针为stdin。从键盘上输入就意味着从标准输入文件上输入数据。如scanf、getchar函数。 • 显示器为标准输出文件,文件指针为stdout。在屏幕上显示有关信息就是向标准输出文件输出。如printf、putchar函数。 • 标准错误输出也是标准设备文件,文件指针为stderr。 • 本章所讲的文件主要是针对普通的磁盘文件。
文件分类 • 从文件编码的方式分: • ASCII码文件。也称为文本文件,在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。 • 二进制文件。数据按其在内存中的存储形式原样存放。
ASCII文件 • 也称文本文件;ASCII文件在磁盘中存放时,每个字符对应一个字节,用于存放对应的ASCII码。如:整型十进制数5678,按ASCII文件存放则需要占用4个字节。
ASCII形式 00110101 00110110 00110111 00111000 内存中的存储形式 00010110 00101110 二进制形式 00010110 00101110 二进制文件 • 是对不同的数据类型,按其实际占用内存字节数存放。即内存的存储形式,原样输出到磁盘上存放。如:整型十进制数5678,按二进制文件存放只需要2个字节。
ASCII 文件(文本文件)形式 00110001 00110010 00110011 00110100 00110101 1 1 A SCII 2 (50) 3 (51) 4 (52) 5 (53) 的 内存存储形式 (49) 码为 00110000 00111001 二进制文件形式 00110000 00111001 7 - 2 图 两种文件形式与内存形式之间的关系 实例 • 十进制整数12345的存储形式如图7-2所示。按ASCII码形式存储共占用5个字节,而采用二进制形式存储只需要2个字节。
ASCII码文件可在屏幕上按字符显示,例如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。ASCII码文件可在屏幕上按字符显示,例如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。 • 由于是按字符显示,因此能读懂文件内容。占用存储空间大,在进行读、写操作时要进行二进制与十进制之间的相互转换,效率低。 • 二进制文件虽然也可在屏幕上显示,但其内容无法读懂。但二进制文件占用存储空间少,在进行读、写操作时不用进行二进制与十进制之间的相互转换,效率高。
文件系统 • 根据操作系统对文件的处理方式的不同,把文件系统分为 • 缓冲文件系统 • 非缓冲文件系统 • ANSI C标准采用缓冲文件系统。
内 存 输入缓冲区 外存 输 输入 入 ( ) 读 程序数据区 文件 输出 输出 ( ) 写 输出缓冲区 7 - 1 图 缓冲文件系统示意图 缓冲文件系统 • 缓冲文件系统是指操作系统在内存中为每一个正在使用的文件开辟一个读写缓冲区。如图7-1所示。
在输入数据时,先把数据从磁盘读到“输入缓冲区”,等输入缓冲区已满或强制把它清空时再把其中的数据送到程序数据区进行处理。在输入数据时,先把数据从磁盘读到“输入缓冲区”,等输入缓冲区已满或强制把它清空时再把其中的数据送到程序数据区进行处理。 • 处理完后的数据要送到文件中保存,先放到“输出文件缓冲区”,等输出缓冲区已满或强制把它清空时再把其中的数据送到磁盘文件。
非缓冲文件系统 • 非缓冲文件系统指系统不自动开辟确定大小的内存缓冲区,而是由程序自己为每个文件设定缓冲区。
在C语言中,没有专门的文件输入输出语句,对文件的读写都是用库函数来实现的。在C语言中,没有专门的文件输入输出语句,对文件的读写都是用库函数来实现的。 • 基本的文件操作都在标准I/O库接口stdio.h中。 • 调用磁盘上的一个文件时,必须知道与该文件有关的信息。比如文件名字、文件的当前读写位置、文件缓冲区大小与位置、文件的操作方式等。这些信息被C语言系统保存在一个称作FILE的结构体中,通常它被放在stdio.h头文件内。
typedef struct { int level; /*缓冲区“满”或“空”的程度*/ unsigned flags; /*文件状态标志*/ char fd; /*文件描述符*/ unsigned char hold;/*如无缓冲区不读取字符*/ int bsize; /*缓冲区大小*/ unsigned char *buffer; /*数据缓冲区位置*/ unsigned char *curp; /*文件定位指针*/ unsigned istemp; /*临时文件指示器*/ short token; /*用于有效性检查*/ }FILE;
文件指针 • 对于每一个要操作的文件,都必须定义一个指针变量,并使它指向该文件结构体变量,这个指针称为文件指针。 • 通过文件指针找到被操作文件的描述信息,就可对它所指的文件进行各种操作。 FILE *指针变量标识符; • 其中FILE应为大写。
文件的操作 • 对文件的操作的一般步骤包括: • 打开文件 • 文件的读写 • 关闭文件 • 在C语言中,文件操作都是由库函数来完成的。这些库函数包含在stdio.h头文件中。
文件的打开与关闭 • 打开文件是建立文件的各种有关信息,并使文件指针指向该文件,以便进行其它各种操作。 • 关闭文件是断开指针与文件之间的联系,禁止再对该文件进行操作。
文件的打开 文件指针名=fopen(文件名,使用文件方式); 如: FILE *fp1; fp1=("c:\\tt\\test.exe","rb"); • 其意义是打开C驱动器磁盘的tt目录下的文件test.exe,这是一个二进制文件,只允许按二进制方式进行读操作。两个反斜线“\\”中的第一个表示转义字符,第二个表示根目录。
文件的打开使用函数fopen(),函数原型为: FILE *fopen(char *filename,char *type); • filename为需要打开的文件名, type为文件打开的类型,也是一个字符串,用于确定文件的使用方式,可以选用的方式如下:
“r”:文件以“读”方式打开,文件指针只能用于输入操作,并且文件必须已经存在。“r”:文件以“读”方式打开,文件指针只能用于输入操作,并且文件必须已经存在。 • “w”:文件以“写”方式打开,文件指针只能用于输出操作,如果文件不存在,将会用指定的文件名创建一个新文件。如果已存在同名文件,那么此同名文件的内容会被删除。 • “a”:文件以“追加”方式打开,此模式与“w”模式相似,返回的指针都可以用于输出操作;它们的区别在于,如果文件已经存在,本方式将不会删除原来的内容,而是向文件的末端写入新信息。 • 文件常用的访问模式如表7-1所示。
文件使用方式 意 义 r 只读打开一个文本文件,只允许读数据 w 只写打开或建立一个文本文件,只允许写数据 a 追加打开一个文本文件,并在文件末尾写数据 rb 只读打开一个二进制文件,只允许读数据 wb 只写打开或建立一个二进制文件,只允许写数据 ab 追加打开一个二进制文件,并在文件末尾写数据 r+ 读写打开一个文本文件,允许读和写 w+ 读写打开或建立一个文本文件,允许读写 a+ 读写打开一个文本文件,允许读,或在文件末追加数据 rb+ 读写打开一个二进制文件,允许读和写 wb+ 读写打开或建立一个二进制文件,允许读和写 ab+ 读写打开一个二进制文件,允许读,或在文件末追加数据
文件的关闭 fclose(文件指针); • 功能:使文件指针变量与文件“脱钩”,释放文件结构体和文件指针。 • 返回值:关闭成功时返回值为0。否则(如磁盘空间不足、写保护或关闭已经关闭的文件)返回EOF,即-1。
文件的关闭使用函数fclose(),函数原型为: int fclose(FILE *stream); • 其中:stream为文件流指针。它的作用是使文件指针变量不再指向该文件,同时关闭文件。当文件正常关闭时,返回0值;当文件关闭失败时,返回非0值。
【例7-2】打开test.txt文件。 #include <stdio.h> #include <stdlib.h> void main() { FILE *fp; /* 定义文件指针 */ fp=fopen("test.txt","w"); /* 以写方式打开文件test.txt */ if(fp==NULL) { printf("file open error!\n"); exit(0); /* 终止程序运行,返回操作系统0值 */ } else printf("fiel open OK!\n"); fclose(fp); /* 关闭文件 */ }
exit函数原型为: void exit(int status); • 功能:终止程序。它将status的值发送给DOS环境变量。它在stdlib.h中声明。
出错检查 • 文件在进行打开操作的时候,并不能保证每次操作都会成功,操作也可能失败。比如,有可能不存在指定的文件进行读操作等。如果需要的文件不存在或者发现其他错误,那么fopen函数调用将返回指针值NULL,表示有错误发生。作为程序员,应该检查错误并且进行相应的错误报告。一般C系统都提供了库函数检测错误,主要错误检测函数如下:
1.错误报告函数的原型为: void error(char *errorstring); 2.文件结束检测错误函数的原型为: int feof(FILE *stream); 3.文件出错检测函数的原型为: int ferror(FILE *stream); 4.清除文件错误标志函数的原型为: void clearerr(FILE *stream);
错误处理 #include <stdio.h> #include <errno.h> #include <io.h> #include <fcntl.h> void main() { /* c:\abc.abc文件并不存在 */ int fh = open("c:\\abc.abc", O_RDONLY | O_BINARY); if (fh == -1) /* fh必然为-1 */ { perror("Can't open c:\\abc.abc. Error"); } } Can't open c:\\abc.abc. Error: No such file or directory
顺序文件的读写 • 文件的顺序读写,是指文件被打开后,按照数据流的先后顺序对文件进行读写操作。每读写一次后,文件指针自动指向下一个读写位置。 • 在C语言中提供了多种文件读写的函数,分别如下: (1)字符读写函数:fgetc和fputc。 (2)字符串读写函数:fgets和fputs。 (3)格式化读写函数:fscanf和fprinf。
fgetc函数的功能是从指定的文件中读一个字符,函数原型为:fgetc函数的功能是从指定的文件中读一个字符,函数原型为: int fgetc(FILE *stream); • fputc函数的功能是把一个字符写入指定的文件中,函数原型为: int fputc(char ch,FILE *stream);
【例7-3】从键盘输入字符存入文件中,以#号结束【例7-3】从键盘输入字符存入文件中,以#号结束 #include <stdio.h> void main() { FILE *fp; char ch,filename[10]; scanf("%s",filename); if ((fp=fopen(filename, "w"))==NULL) /* 建立并打开新文件 */ { printf("cannot open file!\n"); exit(0); /* 终止程序 */ } ch=getchar(); /* 读scanf的回车符 */ ch=getchar(); /* 输入第一个字符 */ while(ch!= '#') /* 如果输入#号则结束 */ { fputc(ch,fp); /* 将ch写入fp所指向的文件 */ putchar(ch); /* 将ch在屏幕上输出,即写入stdin所指向的文件 */ ch=getchar(); } fclose(fp); /* 关闭文件 */ }
fgets函数的功能是从fp所指向的文件中读n-1个字符,放到以string为起始地址的存储空间(string可以是数组名)。函数原型为: char * fgets(char *string, int n, FILE *fp);
【例7-4】从temp.txt文件中读入一个含10个字符的字符串【例7-4】从temp.txt文件中读入一个含10个字符的字符串 #include<stdio.h> void main() { FILE *fp; char str[11]; if((fp=fopen("temp.txt","r"))==NULL) /*以只读方式打开一个文件*/ { printf("Cannot open file strike any key exit!"); getch(); exit(1); /* 打开不成功的处理方式 */ } fgets(str,11,fp); /*打开文件成功,读取10个字符,放在str串中*/ printf("%s",str); /*打印读取字符串*/ fclose(fp); /*操作完成,关闭文件*/ }
fputs函数的功能是将string所表示的字符串内容(不含最后的‘\0’)输出到fp所指向的文件中去。函数原型为: int fputs(char *string, FILE *fp);
【例7-5】向文件string.txt中追加一个字符串。 #include<stdio.h> void main() { FILE *fp; char ch,st[20]; if((fp=fopen("string.txt","a"))==NULL) /* 以追加方式打开一个文件 */ { printf("Cannot open file strike any key exit!"); getch(); exit(1); /* 打开不成功的处理方式 */ } printf("input a string:\n"); scanf("%s",st); /* 从键盘读入一个串 */ fputs(st,fp); /* 将串写入文件中 */ fclose(fp); if((fp=fopen("string.txt","r"))==NULL) { printf("Cannot open file strike any key exit!"); getch(); exit(2); /* 打开不成功的处理方式 */ } ch=fgetc(fp); /* 读取文件中的第一个字符 */ while(ch!=EOF) /* 反复打印读出的字符,再读下一个字符 */ { putchar(ch); ch=fgetc(fp); } printf("\n"); fclose(fp); /* 操作完成,关闭文件 */ }
fscanf函数、fprintf函数与前面使用的scanf和printf函数的功能相似,都是格式化读写函数。 • 两者的区别在于 fscanf函数和fprintf函数的读写对象不是键盘和显示器,而是磁盘文件。
fscanf函数的功能是按照format给出的控制符格式,把从fp所指向的文件中读取的内容,分别赋给变元arg1,…,argn。函数原型为:fscanf函数的功能是按照format给出的控制符格式,把从fp所指向的文件中读取的内容,分别赋给变元arg1,…,argn。函数原型为: int fscanf(FILE *fp, char *format, &arg1, …, &argn)
fprintf函数的功能是按照format给出的控制符格式,将变元arg1,…,argn的值写入到fp所指向的文件中去 。函数原型为: int fprintf(FILE *fp, char *format, arg1, …, argn)
【例7-6】键盘输入两个学生数据,写入一个文件中, 再读出这两个学生的数据显示在屏幕上。 • 源代码见教材P277
问题的实现(例7-1) #include<stdio.h> void main() { FILE *fp1,*fp2; /*定义文件指针*/ 源文件 目标 char ch,*file1,*file2; printf("Input source file :"); scanf("%s",file1); /* 输入源文件名 */ printf("Input object file :"); scanf("%s",file2); /* 输入目标文件名 */ if((fp1=fopen(file1,"r"))==NULL) /*以只读方式打开要拷贝的文件*/ { printf("Cannot open %s\n",file1); getch(); /* 暂停,按任意键继续 */ exit(1); /*文件打开不成功的处理*/ } if((fp2=fopen(file2,"w"))==NULL) /* 以写的方式打开拷贝的目标文件 */ fp2=stdout; /* 缺省输出文件为标准输出设备即到显示器 */ while((ch=fgetc(fp1))!=EOF) /* 读第一个文件的字符 */ fputc(ch,fp2); /* 拷贝到第二个文件 */ fclose(fp1); /* 关闭文件1 */ fclose(fp2); /* 关闭文件2 */ }
#include<stdio.h> void main() { FILE *fp; /*定义文件指针*/ char ch; if((fp=fopen("temp.c","r"))==NULL) /* 以只读方式打开文件 */ { printf("Cannot open file strike any key exit!"); getch(); exit(1); /* 文件打开不成功的处理 */ } ch=fgetc(fp); /* 读文件的第一个字符 */ while (ch!=EOF) /* 读文件没有到文件尾 */ { putchar(ch); /* 输出刚刚读入的字符 */ ch=fgetc(fp); /* 读下一个字符 */ } fclose(fp); /* 关闭文件 */ } 举一反三:读入文件,在屏幕上输出
#include<stdio.h> void main() { FILE *fp; /*定义文件指针*/ char ch; if((fp=fopen("string","wb+"))==NULL) /*以读写方式打开文件*/ { printf("Cannot open file strike any key exit!"); getch(); exit(1);/*文件打开不成功的处理*/ } 从键盘输入一行字符,写入一个文件, 再把该文件内容读出显示在屏幕上。
printf("input a string:\n"); ch=getchar(); /*读入一行字符,每次只读一个*/ while (ch!='\n') { fputc(ch,fp); /*写字符到文件中*/ ch=getchar(); /*读下一个字符*/ } if((fp=fopen("string","rb"))==NULL) /* 以读方式打开文件 */ { printf("Cannot open file strike any key exit!"); getch(); exit(2); /*文件打开不成功的处理*/ } ch=fgetc(fp); /*读第一个字符*/ while(ch!=EOF) { putchar(ch); /*输出字符*/ ch=fgetc(fp); /*读下一个字符*/ } printf("\n"); fclose(fp); /*关闭文件*/ }