一、文件的基本概念 第十三章 文 件 所谓“文件”是指一组相关数据的有序集合。 这个数据集有一 第十三章 文 件 一、文件的基本概念 所谓“文件”是指一组相关数据的有序集合。 这个数据集有一 个名称,叫做文件名。 实际上在前面的各章中我们已经多次使用了文件,例如源程序文件、目标文件、可执行文件、库文件 (头文件)等。 1、从用户的角度看,文件可分为普通文件和设备文件两种。 普通文件是指驻留在磁盘或其它外部介质上的一个有序数据集,可以是源文件、目标文件、可执行程序; 也可以是一组待输入处理的原始数据,或者是一组输出的结果。对于源文件、目标文件、 可执行程序可以称作程序文件,对输入输出数据可称作数据文件。
设备文件是指与主机相联的各种外部设备,如显示器、打印机、 键盘等。在操作系统中,把外部设备也看作是一个文件来进行管理, 把它们的输入、输出等同于对磁盘文件的读和写。 通常把显示器定 义为标准输出文件, 一般情况下在屏幕上显示有关信息就是向标准 输出文件输出。如前面经常使用的printf,putchar 函数就是这类输 出。键盘通常被指定为标准的输入文件, 从键盘上输入就意味着 从标准输入文件上输入数据。scanf,getchar函数就属于这类输入。 2、从文件编码的方式来看,文件可分为ASCII码文件 和二进制码文件两种。 ASCII文件也称为文本文件,这种文件在磁盘中存放时每个 字符对应一个字节,用于存放对应的ASCII码。 例如,数5678的存储形式为:
ASCII码: 00110101 00110110 00110111 00111000 ↓ ↓ ↓ ↓ 十进制码: 5 6 7 8 共占用4个字节。ASCII码文件可在屏幕上按字符显示, 例 如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。 二进制文件是按二进制的编码方式来存放文件的。 例如, 数5678的存储形式为: 00010110 00101110 只占二个字节。二进制文件虽然也可在屏幕上显示, 但其 内容无法读懂。
C系统在处理这些文件时,并不区分类型,都看成是字符 流,按字节进行处理。 输入输出字符流的开始和结束只由程序控 制而不受物理符号(如回车符)的控制。 因此也把这种文件称作 “流式文件”。 本章讨论流式文件的打开、关闭、读、写、定位等各种 操作。
二、文件指针 在C语言中用一个指针变量指向一个文件, 这个指针称为文件 指针。通过文件指针就可对它所指的文件进行各种操作。 定义说明 文件指针的一般形式为: FILE *指针变量标识符; 其中FILE应为大写,它实际上是由系统定义的一个结构, 该结 构中含有文件名、文件状态和文件当前位置等信息。在编写源程序 时不必关心FILE结构的细节。例如: FILE *fp; 表示fp是指向FILE结构的指针变量,通过fp 即可找存放某个文 件信息的结构变量,然后按结构变量提供的信息找到该文件, 实施 对文件的操作。习惯上也笼统地把fp称为指向一个文件的指针。
三、文件的打开与关闭 文件在进行读写操作之前要先打开,使用完毕要关闭。 所谓打 开文件,实际上是建立文件的各种有关信息, 并使文件指针指向该 文件,以便进行其它操作。关闭文件则断开指针与文件之间的联系, 也就禁止再对该文件进行操作。 在C语言中,文件操作都是由库函数来完成的。 在本章内将介 绍主要的文件操作函数。
1、文件打开函数fopen fopen函数用来打开一个文件,其调用的一般形式为: 文件指针名=fopen(文件名,使用文件方式) 其中,“文件指针名”必须是被说明为FILE 类型的指针变量, “文件名”是被打开文件的文件名。 “使用文件方式”是指文件的 类型和操作要求。“文件名”是字符串常量或字符串数组。 例如: FILE *fp; fp=fopen("a1","r"); 其意义是在当前目录下打开文件a1, 只允许进行“读”操 作,并使fp指向该文件。又如: FILE *fphzk fphzk=fopen("c:\\hzk16',"rb")
使用文件的方式共有12种,下面给出了它们的符号和意义。 文件使用方式 意 义 “r” 只读 打开一个文本文件,只允许读数据 “w” 只写 打开或建立一个文本文件,只允许写数据 “a” 追加 打开一个文本文件,并在文件末尾写数据 “rb” 只读 打开一个二进制文件,只允许读数据 “wb” 只写 打开或建立一个二进制文件,只允许写数据 “ab” 追加 打开一个二进制文件,并在文件末尾写数据 “r+” 读写 打开一个文本文件,允许读和写 “w+” 读写 打开或建立一个文本文件,允许读写 “a+” 读写 打开一个文本文件,允许读,或在文件末追加数 据 “rb+” 读写 打开一个二进制文件,允许读和写 “wb+” 读写 打开或建立一个二进制文件,允许读和写 “ab+” 读写 打开一个二进制文件,允许读,或在文件末追加 数据
说明: 1. 凡用“r”打开一个文件时,该文件必须已经存在, 且只能从该 文件读出。 2. 用“w”打开的文件只能向该文件写入。 若打开的文件不存在, 则以指定的文件名建立该文件,若打开的文件已经存在,则将该 文件删去,重建一个新文件。 3. 若要向一个已存在的文件追加新的信息,只能用“a ”方式打开 文件。但此时该文件必须是存在的,否则将会出错。 4. 用"r+"、"w+"、"a+"方式打开的文件可以用来输入或输出。
5. 在打开一个文件时,如果出错,fopen将返回一个空指针值NULL 。在程序中可以用这一信息来判别是否完成打开文件的工作,并作 相应的处理。因此常用以下程序段打开文件: if((fp=fopen("file1","rb")==NULL) { printf("\nerror on open this file!"); exit(0); } 这段程序的意义是,如果返回的指针为空,表示不能打开文 件,给出提示信息“error on open this file!”, 执行exit(0) 退出程序。
6. 把一个文本文件读入内存时,要将ASCII码转换成二进制码, 而 因此文本文件的读写要花费较多的转换时间。对二进制文件的读 写不存在这种转换。 7. 标准输入文件(键盘),标准输出文件(显示器 ),标准出错输出 (出错信息)是由系统打开的,可直接使用。系统自动定义了三个 文件指针stdin、stdout、stderr分别指向它们。
2、文件关闭函数fclose 文件一旦使用完毕,应用关闭文件函数把文件关闭, 以避免文件的数据丢失等错误。 例如: fclose(fp); 正常完成关闭文件操作时,fclose函数返回值为0。如返回非零值则表示有错误发生。
四、文件的读写 对文件的读和写是最常用的文件操作。 在C语言中提供了多种 文件读写的函数: ·字符读写函数 :fgetc和fputc ·字符串读写函数:fgets和fputs ·数据块读写函数:fread和fwrite ·格式化读写函数:fscanf和fprinf 下面分别予以介绍。使用以上函数都要求包含头文件stdio.h。
1、字符读写函数fgetc和fputc 读字符函数fgetc 字符读写函数是以字符(字节)为单位的读写函数。 每次可从文 件读出或向文件写入一个字符。 读字符函数fgetc fgetc函数的功能是从指定的文件中读一个字符,读取的文件必 须是以读或读写方式打开的。 函数调用的形式为: 字符变量=fgetc(文件指针); 例如: ch=fgetc(fp); 其意义是从打开的文件fp中读取一个字符并送入ch中。
如果在执行fgetc读字符时遇到文件结束符,函数返回一个文件结束标志EOF。EOF在stdio.h中定义为-1。 如果想从磁盘文件顺序读入字符并在屏幕上显示出来,可以: ch=fgetc(fp); while (ch!=EOF) { putchar(ch); }
读入文件e1.c,在屏幕上输出。 #include<stdio.h> main() { FILE *fp; char ch; if((fp=fopen("e1.c","r"))==NULL) { printf("Cannot open file!"); exit(0); } ch=fgetc(fp); while (ch!=EOF) { putchar(ch); fclose(fp);
写字符函数fputc fputc函数的功能是把一个字符写入指定的文件中,函数调用 的形式为: fputc(字符量,文件指针); 其中,待写入的字符量可以是字符常量或变量,例如: fputc('a',fp); 其意义是把字符a写入fp所指向的文件中。 fputc函数有一个返回值,如写入成功则返回写入的字符, 否 则返回一个EOF(-1)。可用此来判断写入是否成功。
从键盘输入一行字符,写入一个文件, 再把该文件内容读出显示在屏幕上。 #include<stdio.h> main() { FILE *fp; char ch; if((fp=fopen("string","w+"))==NULL) printf("Cannot open file!"); exit(0); } ch=getchar(); while (ch!='\n') { fputc(ch,fp); rewind(fp); ch=fgetc(fp); while(ch!=EOF) { putchar(ch); } printf("\n"); fclose(fp);
2、格式化读写函数fscanf和fprintf fscanf函数,fprintf函数与前面使用的scanf和printf 函数的 功能相似,都是格式化读写函数。 两者的区别在于 fscanf 函数和 fprintf函数的读写对象不是键盘和显示器,而是磁盘文件。这两个 函数的调用格式为: fscanf(文件指针,格式字符串,输入表列); fprintf(文件指针,格式字符串,输出表列); 例如: fscanf(fp,"%d%s",&i,s); fprintf(fp,"%d%c",j,ch);
例如: fprintf(fp,"%d,%6.2f",i,t); 的作用是将变量i和实型变量t的值 格式输出到fp指向的文件。 如i=3,t=4.5 则输出到文件上的字符串是 3, 4.5 同样,用以下fscanf函数可以从文件上读入ASCII字符: fscanf(fp,%d,%f",&i,&t); 完成将文件中的3送给变量i,4.5送给变量t。
3、数据块读写函数fread和fwrite C语言还提供了用于整块数据的读写函数。 可用来读写一组数 据,如一个数组,一个结构变量的值等。 读数据块函数调用的一般形式为: fread(buffer,size,count,fp); 写数据块函数调用的一般形式为: fwrite(buffer,size,count,fp); 其中buffer是一个指针,在fread函数中,它表示存放输入数据 的首地址。在fwrite函数中,它表示存放输出数据的首地址。 size 表示数据块的字节数。 count 表示要读写的数据块块数。 fp 表示文件指针。
例如: fread(fa,4,5,fp); 其意义是从fp所指的文件中,每次读4个字节(一个实数)送入 实数组fa中,连续读5次,即读5个实数到fa中。
1. 一个学习小组有5个人,每个人有三门课的考试成绩。求全 组分科的平均成绩和各科总平均成绩。 Math C DBASE 张 80 75 92 王 61 65 71 李 59 63 70 赵 85 87 90 周 76 77 85 可设一个二维数组a[5][3]存放五个人三门课的成绩。 要求: 将结果写入文件myf1.out中
main( ) { int i,j,s=0,l,v[3]; static int a[5][3]={ {80,75,92},{61,65,71},{59,63,70}, {85,87,90},{76,77,85} }; for(i=0;i<3;i++) { for(j=0;j<5;j++) s=s+a[j][i]; v[i]=s/5; s=0; } l=(v[0]+v[1]+v[2])/3; printf("math:%d\nc languag:%d\ndbase:%d\n",v[0],v[1],v[2]); printf("total:%d\n",l); FILE *fp; int I;
if ((fp=fopen(“myf1”,”wb”))==NULL) printf(“cannot open file\n”); exit(0); for(I=0;I<3;i++) if (fwrite(v,4,1,fp)!=1) printf(“file write error\n”); fwrite(&l,4,1,fp); fclose(fp); }
移动文件内部位置指针的函数主要有两个, 即 rewind 函数和 fseek函数。 1、rewind函数 rewind函数前面已多次使用过,其调用形式为: rewind(文件指针); 它的功能是把文件内部的位置指针移到文件首。
五、文件的定位 前面介绍的对文件的读写方式都是顺序读写, 即读写文件只能 从头开始,顺序读写各个数据。 但在实际问题中常要求只读写文件 中某一指定的部分。 为了解决这个问题可移动文件内部的位置指针 到需要读写的位置,再进行读写,这种读写称为随机读写。 实现随 机读写的关键是要按要求移动位置指针,这称为文件的定位。
2、fseek函数 fseek函数用来移动文件内部位置指针,其调用形式为: fseek(文件指针,位移量,起始点); 其中:“文件指针”指向被移动的文件。 “位移量”表示移动 的字节数,要求位移量是long型数据,以便在文件长度大于64KB 时 不会出错。当用常量表示位移量时,要求加后缀“L”。
“起始点”表示从何处开始计算位移量,规定的起始点有三种: 文件首,当前位置和文件尾。其表示方法如下表 起始点 表示符号 数字表示 ────────────────────────── 文件首 SEEK—SET 0 当前位置 SEEK—CUR 1 文件末尾 SEEK—END 2 例如: fseek(fp,100L,0); 其意义是把位置指针移到离文件首100个字节处。 还要说明的是fseek函数一般用于二进制文件。在文本文件中由 于要进行转换,故往往计算的位置会出现错误。