第十三讲 文件
?C文件概述
?文件类型指针
?文件的打开与关闭
?文件的读写
?文件的定位
?出错的检测
?非缓冲文件系统
?文件输入输出小结
C文件概述
文件 (file)一般指,存储在外部介质上数据的集合。数据是
以文件的形式存放在外部介质 (如磁盘 )上的,操作系统是
以文件为单位对数据进行管理的,也就是说,如果想找存在外
部介质上的数据,必须先按文件名找到所指定的文件,然后再
从该文件中读取数据。要向外部介质上存储数据也必须先
建立一个文件 (以文件名标识 ),才能向它输出数据。
C语言把文件看作是一个字符 (字节 )的序列,即由一个一
个字符 (字节 )的数据顺序组成,根据数据的组织形成,可分为
ASCII文件和二进制文件。 ASCII文件又称文本 (text)文件,它
的每一个字节放一个 ASCII代码,代表一个字符,二进制文件
是把内存中的数据按其在内存中的存储形式原样输出到磁
盘上存放。
ASCII形式和二进制形式输出的不同,
例, 如果有一个整数 10000,在内存中占 2个字节,如果按
ASCII形式输出,则占 5个字节,而按二进制形式输出,在
磁盘上只占 2个字节,如下图所示,
00100111 00010000
00100111 00010000
00110001 00110000 00110000 00110000 00110000
ASCII形式
(1) (0) (0) (0) (0)
二进制形式
由前所述,一个 C文件是一个字节流或二进制流。它把数
据看作是一连串的字符 (字节 ),而不考虑记录的界限,即 C中
文件并不是由记录 (record)组成的。在 C中对文件的存取是
以字符 (字节 )为单位的,输入 /输出数据流的开始和结束仅
受程序控制而不受物理符号控制,我们把这种文件称为流
式文件。






式,
文件的处理方法
旧 C版本有两种对文件的处理方法,
一,缓冲文件系统,
数据程序区
输出文件缓冲区
输入文件缓冲区
输出
输入
输出
输入
磁盘
所谓缓冲文件系统是指,系统自动
地在内存区为每一个正在使用的文
件名开辟一个缓冲区 。 从内存向磁
盘输出数据必须先送到内存中的缓
冲区,装满缓冲区后才一起送到磁
盘去;从磁盘向内存读入数据,也
是一次从磁盘文件将一批数据输入
到内存缓冲区,然后再从缓冲区逐
个地将数据送到程序数据区 (给程
序变量 )。 缓冲区的大小由各个具
体的 C版本确定,一般为 512字节 。
二,非缓冲文件系统, 所谓“非缓冲文件系统”是指系统
不 自动开辟确定大小的缓冲区,
而由程 序为每个文件设定缓冲区。
在 UNIX系统中,用缓冲文件系统来处理文本文件,用非
缓冲文件系统处理二进制文件。用缓冲文件系统进行的
输入 /输出又称为高级输入 /输出系统,用非缓冲文件系统
进行的输入输出又称为低级输入 /输出系统。 TC只采用
缓冲文件系统,即既用缓冲文件系统处理文本文件,也用
它来处理二进制文件。
文件类型指针
缓冲文件系统中,关键的概念是“文件指针”。每个被使用的
文件都在内存中开辟一个区域,用来存放文件的有关信息 (如文件
的名字,文件状态及文件当前位置等 )。这些信息是保存在一个结
构体类型变量中的,该结构体类型由系统定义,取名为 FILE。一
般的 C中,在 stdio.h文件中有以下的类型定义,
typedef struct
{ int _fd; /*文件号 */
int _cleft; /*缓冲区中剩下的字符 */
int _mode; /*文件操作模式 */
char *_nextc; /*下一个字符位置 */
char *_buff; /*文件缓冲区位置 */
} FILE;
有了 FILE类型之后,可以用它来定义若干 FILE类型的变
量,以便存放若干个文件的信息。例如,可以定义文件型指
针变量,如
FILE *fp;
fp是一个指向 FILE类型结构体的指针变量, 可以使 fp指向
一个文件的结构体变量,从而通过该结构体变量中的文件信
息能够访问该文件, 也就是说,通过文件指针变量能够找到
与它相关的文件 。 如果有 n个文件,一般应设 n个指针 (指向
FILE类型结构体的指针变量 ),使它们分别指向 n个文件 (确切
地说,指向该文件的信息结构体 ),以实现对文件的访问 。
文件的打开 (fopen函数 )
与其它高级语言一样,对文件读写之前应该“打开”
该文件,在使用结束之后应关闭该文件。
fopen函数的调用方式为,
FILE *fp;
fp=fopen(文件名,使用文件方式 );
例如,
fp=fopen(“A1”,”r”)
它表示,要打开名字为 A1的文件,使用文件方式为“读
入”,fopen函数带回指向 A1文件的指针并赋给 fp,这样
fp就和 A1相联系了,或者说,fp指向 A1文件。
可以看出,在打开一个文件时,通知给编译系统以下三
个信息,
(1)需要打开的文件名,也就是准备访问的文件的
名字。
(2) 使用文件的方式 (读还是写等 )。
(3) 让哪一个指针变量指向被打开的文件 。
文件使用方式 含义
, r” (只读 ) 为输入打开一个文本文件
, w” (只写 ) 为输出打开一个文本文件
, a” (追加 ) 向文本文件尾增加数据
, rb” (只读 ) 为输入打开一个二进制文件
, wb” (只写 ) 为输出打开一个二进制文件
, ab” (追加 ) 向二进制文件尾增加数据
, r+” (读写 ) 为读 /写打开一个文本文件
, w+” (读写 ) 为读 /写建立一个新的文本文件
, a+” (读写 ) 为读 /写打开一个文本文件
, rb+” (读写 ) 为读 /写打开一个二进制文件
, wb+” (读写 ) 为读 /写建立一个新的二进制文件
, ab+” (读写 ) 为读 /写打开一个二进制文件
使用文件方式如下表,
说明
1.用,r,方式打开的文件只能用于向计算机输入而不能用作
向该文件输出数据。而且该文件应该已经存在,否则出错。
2.用,w,方式打开的方式只能用于向该文件写数据,而不能用
来向计算机输入。如果原来不存在该文件,则在打开时新建立
一个以指定名字命名的文件;如果原来已存在一个以该文件
名命名的文件,则在打开时将该文件删去,然后重新建立一个新
文件。
3.如果希望向文件末尾添加新的数据 (不希望删除原有数据 ),则
应该用,a,方式打开。若该文件不存在,则先建立该文件。
4.用,r+”,”w+”,”a+”方式打开的文件可以用来输入和输出数据。
5.如果不能实现“打开”的任务,fopen函数将会带回一个出
错信息,出错的原因可能是,用,r,方式打开一个并不存在
的文件 ;磁盘出故障 ;磁盘已满无法建立新文件等,此时 fopen
函数将带回一个空指针值 NULL (NULL在 stdio.h文件中已被
定义为 0)。
常用下面的方法打开一个文件,
if ((fp=fopen(“file1”,“r”))= =NULL)
{ printf(“cannot open this file\n”);
exit ( 0 );
}
即先检查打开有否出错,如果有错就在终端上输出,cannot
open this file”,exit函数的作用是关闭所有文件,终止正调用
的过程 。 待程序员检查出错误,修改后再运行 。
6.在读写文本文件时,将回车和换行符转换为一个换行符,在
输出时把换行符转换成为回车和换行两个字符。二进制文件
则不进行这种转换,在内存中的数据形式与输出到外部文件
中的数据形式完全一至,一一对应,
7.在程序开始运行时,系统自动打开三个标准文件,标准输入,
标准输出,标准出错输出。通常这三个文件都与终端相联系,
系统自动定义了三个文件指针 stdin,stdout和 stderr,分别指向
终端输入,终端输出和标准出错输出 (也从终端输出 )。如果程
序中指定要从 要 stdin所指的文件输入数据,就是指从终端键
盘输入数据,
文件的关闭 (fclose函数 )
,关闭”就是使文件指针变量不指向该文件,也就是文件指针
变量与文件“脱钩”,此后不能再通过该指针对其相连的文件
进行读写操作,除非再次打开,使指针变量重新指向该文件。
fclose函数调用的一般形式为,
fclose (文件指针 );
例如,
fclose (fp);
用 fopen函数打开文件时所带回的指针赋给了 fp,今把该文件关
闭。
应该养成在程序终止之前关闭所有使用的文件的习惯,如果不
关闭文件将会丢失数据。
fclose 函数也带回一个值,当顺利地执行了关闭操作,则返回值
为 0;如果返值为非零值,则表示关闭时有错误,可以用 ferror函数
来测试。
fputc函数和 fgetc函数
也称 putc函数和 getc函数,
一,fputc函数把一个字符写到磁盘文件上去,其一般形式为
fputc ( ch,fp );
其中 ch是要输出的字符,它可以是一个字符常量,也可以是一个
字符变量。 fp是文件指针变量,它从 fopen函数得到返回值。
fputc函数也带回一个值,如果输出成功则返回值就是输出的字
符,如果输出失败,则返回一个 EOF。 EOF是在 stdio.h文件中定
义的符号常量,值为 -1。
二,fgetc函数
从指定文件读入一个字符,该文件必须是以读或读写方
式打开的,fgetc函数的调用形式为
ch=fgetc ( fp );
fp为文件型指针变量,ch 为字符变量,fgetc函数带回一个字符,
赋给 ch.如果在执行 fgetc读字符时遇到文件结束符,函数返回
一个文件结束标志 EOF。如果想从一个磁盘文件顺序读入
所有字符并在屏幕上显示出来,可以,
ch=fgetc ( fp );
while(ch!=EOF)
{ putchar(ch);
ch=fgetc(fp);
}
TC提供一个 feof函数来判断文件是否真的结束。如果是文
件结束,函数 feof(fp)的值为 1(真 ),否则为 0(假 ),
如果想顺序读入一个二进制文件中的数据,可以用,
while(!feof(fp))
{ c=fgetc(fp);
,.,
}
注意, 这种方法也适用于文本文件。
例 L9-1,从键盘输入一些字符,逐个把它们送到磁盘上去,直
到输入一个,#”为止。
例 L9-2,文件复制。
本程序是按文本文件方式处理的,也可以用此程序来复制
一个二进制文件,只需将两个 fopen函数中的,r”和,w”分
别改为,rb”和,wb”即可。
例 L9-2-2,用命令行参数进行文件复制。(可在 DOS命令行
状态下运行,相当于 copy命令。)
fread函数和 fwrite函数
用 getc和 putc函数可以用来读写文件中的一个字符。但是
常常要求一次读入一组数据 (例如,一个实数或一个结构体变
量的值 ),ANSI C 标准提出设置两个函数 (fread和 fwrite),用来
读写一个数据块,它们的一般调用形式为,
fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);
其中,
buffer,是一个指针,对 fread来说,它是读入数据的存放地址,
对 fwrite来说,是要输出数据的地址 (以上指的是起
始地址 )
size,要读写的字节数,
count,要进行读写多少个 size字节的数据项,
fp,文件型指针,
如果文件以二进制形式打开,用 fread和 fwrite函数就可以读写
任何类型的信息,如,
fread(f,4,2,fp);
其中 f是一个实型数组名,一个实型变量占 4个字节,这个函数从
fp所指向的文件读入 2次 (每次 4个字节 )数据,存储到数组 f中,
如果有一个如下的结构体类型,
struct student-type
{ char name[10];
int num;
int age;
char addr[30];
} stud[40]; /*每一个元素用一存放一个学生的数据 */
假设学生的数据已存放在磁盘文件中,可以用下面的 for语句和
fread函数读入 40个学生的数据,
for(i=0;i<40;i++)
fread(&stud[i],sizeof(struct student_type),1,fp);
同样,以下 for语句和 fwrite函数可以将内存中的学生数据输出到磁
盘文件中去。
for(i=0;i<40;i++)
fwrite(&stud[i],sizeof(struct student_type),1,fp);
如果 fread或 fwrite调用成功,则函数返回值为 count的值,即输入或输
出数据项的完整个数。
注意, fread和 fwrite函数一般用于二进制文件的输入输出,因为它们
是按数据块的长度来处理输入输出的,在字符发生转换的情况下很
可能出现与原设想的情况不同,
例题分析
例 L9-3,从键盘输入 n个学生的有关数据,然后把它们转存到磁
盘文件上去。
例 L9-3-2,从文件中读所有学生的有关数据,显示在屏幕上。
例 L9-3-3,向文件中中追加学生数据。
例 L9-4统计文件中单词的个数。
fgets函数和 fputs函数
fgets的作用是从指定文件读入一个字符串,如
fgets(str,n,fp);
从 fp 指向的文件输入 n-1个字符,并把它们放到字符数组 str中,如
果在读入 n-1 个字符结束之前遇到换行符或 EOF,读入即结束,字符
串读入后在最后加一个‘ \0’字符,fgets函数返回值为 str的首地址。
fputs函数的作用是向指定的文件输出一个字符串,如
fputs(,China”,fp);
把字符中,China” 输出到 fp指向的文件 (‘ \0’不输出)。 fputs函
数中第一个参 数可以是字符串常量,字符数组名或字符型指针,
输出成功,函数值为 0; 失败时,为非零值 。
例 L9-5:从键盘输入若干行字符串到文件。
例 L9-6:显示文件内容。 例 L9-7:改进 L9-6 为 Type命令。
fprintf函数和 fscanf函数
fprintf函数,fscanf函数与 printf函数,scanf函数作用相仿,都是
格式化读写函数,只有一点不同,fprintf和 fscanf函数的读写对
象不是终端而是磁盘文件,一般调用方式为,
fprintf(文件指针,格式字符串,输出表列 )
fscanf(文件指针,格式字符串,输入表列 )
例如,
printf(fp,” %d,%6.2f”,i,t);
它的作用是将整型变量 I和实型变量 t的值按 %d和 %6.2f的格
式输出到 fp指向的文件上,如果 I=3,t=4.5,则输出到磁盘文件
上的是以下的字符串,
3,4,50
同样,用以下 fscanf函数可以从磁盘文件上读入 ASCII字符,
fscanf(fp,” %d,%f”,&i,&t);
磁盘文件上如果有以下字符,
3,4,5
则将磁盘文件中的数据 3送给变量 I,4,5送给变量 t,
用 fprintf和 fscanf函数对磁盘文件读写,使用方便,容易
理解,但由于在输入时要将 ASCII码转换为二进制形式,在输
出时又要将二进制形式转换成字符,花费时间比较多,因此,
在内存与磁盘频繁交换数据的情况下,最好不用 fprintf 和
fscanf函数,而用 fread和 fwrite函数,
文件的定位
文件中有一个位置指针,指和当前读写的位置。如果顺序
读写一个文件,每次读写完一个字符后,该位置指针自动移动
指向下一个字符位置,如果想改变这样的规律,可以用有关函
数,强制使位置指针指向其它指定的位置 。
rewind函数
rewind函数的作用是使位置指针重新返回文件的开头,此函
数没有返回值,
例 L9-9-2,有一个磁盘文件,第一次使它显示在屏幕上,第二
次把它复制到另一个文件中。( 9-9-3没有末尾符号)
#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(p1)) putc(getc(fp1),fp2);
fclose(fp1); fclose(fp2);
}
fseek函数和随机读写
对流式文件可以进行顺序读写,也可以进行随机读定,关
键在于控制文件的位置指针,如果位置指针是按字节位置顺
序移动的,就是顺序读写,如果可以将位置指针按需要移动到
任意位置,就可以实现随机读写,所谓随机读写,是指读写完
上一个字符 (字节 )后,并不一定要读写其后续的字符 (字节 ),
而可以读写文件中任意所需的字符 (字节 ),
用 fseek函数可以实现改变文件的位置指针,
fseek 函数的调用形式为
fseek ( 文件类型指针,位移量,起始点 )
,起始点” 用 0,1或 2代替,0代表,文件开始”,1为,当前位
置”,2为,文件末尾” 。 TC指定以下的名字,
起始点 名字 用数字代表
文件开始 SEEK_SET 0
文件当前位置 SEEK_CUR 1
文件末尾 SEEK_END 2
,位移量” 指以,起始点” 为基点,向前移动的字节数,一般要求
位移量是 long 型数据。这样当文件的长度大开 64K进不致出问题。
Fseek函数一般用于二进制文,因为文本文要发生字符转换,计
算位置时往往会发生混乱。
调用 fseek如下,
fseek(fp,100L,0);将位置指针到离文件头 100个字节处
fseek(fp,50L,1); 将位置指针移到当前位置 50个字节处
fseek(fp,-10L,2);将位置指针从文件末尾处后退 10个字节
ftell函数
fetll函数的作用是得到流式文件中的当前位置,用相对而
于文件开头的位置量来表示。由于文件中的位置指针经常移
动,人们不容易辨清其当前位置,用 ftell函数可以得到当前位置,
如果 ftell 函数返回 值 为 -1,表示出错 。 例如,
i=ftell(fp);
if (i= =-1) printf(“error\n”);
变量 i存放当前位置,如调用函数出错 (如不存在此文件 ),则输出
,error”,
ferror函数
在调用各种输入输出函数 (如 putc,getc,fread,fwrite等 )时,如果出现错
误,除了函数返回值有所反映外,还可以用 ferror 函数检查。它的一般
调用形式为,
ferror(fp);
如果 ferror返回值为 0(假 ),表示未出错,如果返回一个非零值,表
示出错。
应该注意,对周一个文件每一次调用输入输出函数,均产生一个
新的 ferror 函数值,因此,应当在调用一个输入输出函数后立即检查
ferror函数的值,否则信息会丢失。
在执行 fopen函数时,ferror函数的初始值自动置于 0。例 L9-9-4
chearerr函数
它的作用是使文件错误标志和文件结束标志置为 0.假如
在调用一个输入输出函数时出现错误,ferror函数值为一个非
零值,在调用 clearerr(fp)后,ferror(fp)的值变成 0,
只要出现错误标志,就一直保留,直到对同一文件调用
clearerr函数或 rewind函数,或任何它一个输入输出函数,或任
何其它一个输入函数。
非缓冲文件系统
缓冲输入输出系统又称高级磁盘输入输出系统,非缓冲输
入输出系统又称为低级磁盘输入输出系统,系统不为这类文件
自动提供文件缓冲区,程序设计必须自己设定一个缓冲区并考
虑如何使用它们,非缓冲文件系统提供了一些输入输出函数,用
于对这进行输入输出操作,
缓冲文件系统 (高级 I/O系统 )是有文件指针的,通过文件指
针访问文件,而非缓冲文件系统 (低级磁盘 I/O系统 )则没有文件
型指针,不是靠文件指来访问文件,而是一个整数代表一个文件
(相当于 FORTRAN等语言的“文件号” ),这个整数称为“文件
说明符,”通过下面介绍的几个常用函数可以对这种方式有一
大概的了解,
open 函数
用来打开一个非缓冲文件,它的一般形式为
open (文件名,打开方式 )
打开方式指该文件打开后的工作方式,即读写方式,
方式 作用
0 只能读
1 只能写
2 可以读 /写
例如,open(“Li_1”,0),打开一个名为,Li_1”的文件,只
能用于输入,
如果打开成功,open函数返回一个正整数 ;如果文件夹
因故未能打开,则返回 -1.open函数一般是这样使用的 (设 fd
已定义为整形变量),
if((fd=open (“A”,1)= =-1)
{ printf(,cannot open file /n”);
exit(0);
}
如果要打开的文件不存在,多数C编译按“打开失
败”处理,不产生新的文件.但有的编译可以用 open 函
数建立一个新文件,例如,上面的打开操作中,如果原来磁盘
上不存在一个名为,A”的文件,则 open函数建立一个为,A”
的文件,可供写数据,另一些 C编译系统则只能用 creat 函数
建立 一个新文件,
close 函数
用来关闭已打开的文件,其调用形式为
close(fd);
fd 为整型变量,它是“文件说明符” (即文件号 ).在打开文件
时,open函数返回一个整数,这就是“文件说明符”(文件
号).在未关闭此文件之前,此文件说明符与该文件相联系,
或者说,它代表一个确定的文件.执行 close 函数后,文件号释
放,它不再与一个确定 的文件相联系.它可以再被用来与另一
文件相联系.文件号是由系统在打开时分配的,而不是由程序
设计设计者指定的.每一个C编译系统规定了可以打开的文件
的最大数字,
由于一个C编译系统允许同时打开的数目是有限的.因此,
凡不再使用的文件应及时用 close 函数关闭,
如果关闭操作失败(如不存在此文件,可把磁盘从驱动取
出),则 close 函数返回-1;成功时返回零,
creat函数
有的 C编译系统 (例如 Turbo C)不允许用 open函数建立一个
新文件,它提供 creat函数用来建立新文件,其调用形式为
creat(文件名,打开方式 );
它返回一个整数文件号,例如,
fd=creat(“A”,1);
read函数
read 函数的作用是从指定的磁盘文件中读入若干个字符
到程序开辟的缓冲区中,
read(fd,buf,count);
其中 fd 为文件号,buf 是一个地址,它指向程序员指定的“缓冲
区”可以是一人数组或一个变量,或一个已分配内存的数据结
构,count是一个整数,上述 read函数的作用是从 fd所代表的文
件中文件中,读 count个字节的信息到 buf指向的缓冲的缓冲区
中,如果执行 read成功,则函数返回实际读入的字节数 ;若遇文
件结束则函数值返回 0;若有错,返回- 1,
write 函数
作用是从指定的内存区将若干个字节的信息输出到指定的
文件中。其一般形式为
write(fd,buf,count);
其中 fd,buf,和 count 的含义与 read函数中相同。上述 write 函数
的作用是:从 buf 所指向的内存中区中输出 count 个字节的信
息到 fd所代表的磁盘文件中去。 Write函数的返回值为实际输出
的字节数。如果实际输出的字节数比的指定的 count 小,则函
数值可能比 count 小。如果执行 write有错误,则返回值为 -1 。
lseek函数和随机读写
用来移动文件位置指针,其调用形式为
lseek(文件号,位移量,起始点 )
其概念大致与缓冲文件系统的 fseek函数相同,只是不用文件指
针而用文件号来标识文件,,起始点”用 0,1和 2分别代表“文件
开始位置”,文件“当前位置”和“文件末尾”,函数的返回值
是长整型 (long型 ).例如,
lseek(fd,100L,0); 将文件位置指针移到离文件开头 100字节处
lseek(fd,-10L,1) 将文件位置指针从当前位置倒退 10个字节
如果执行 lseek函数失败,则返回 -1L(注意 -1L代表长整数 -1)
有了 lseek函数就能够实现对非缓冲文件的随机存取,只需事先
将文件位置指针移到所需读写的位置上,然后进行读写操作即
可,
常用的缓冲文件系统函数
分类 函数名 功能
打开文件 fopen( ) 打开文件
关闭文件 fclose( ) 关闭文件
文件定位 fseek( ) 改变文件位置指针位置
fewind( ) 使文件位置指针重新置于文
件开头
ftell( ) 返回文件位置指针的当前值
文件读写 fgetc( ),getc( ) 从指定文件取得一个字符
gputc( ),putc( ) 把字符输出到指定文件
fgets( ) 从指定文件读取字符串
fputs( ) 把字符串输出到指定文件
分类 函数名 功能
getw( ) 从指定文件读取一个字 (int型 )
putw( ) 把一个字 (int型 )输出到指定文件
fread( ) 从指定文件中读取数据项
fwrite( ) 把数据项写到指定文件
fscanf( ) 从指定文件按格式输入数据
fprintf() 按指定格式将数据写到指定文件中
文件状态 feof( ) 若到文件末尾,函数值为“真” (非
0)
ferror( ) 若对文件操作作出错,函数值为,
真,(非 0)
clearerr() 使 ferror和 feof函数值置零
续,
文件读写
常用的非缓冲文件文件函数
分类 函数名 功能
建立文件 creat( ) 建立一个原来不存在的文件
打开文件 open( ) 打开已有文件
关闭文件 close( ) 关闭已打开的文件
文件定位 lseek( ) 移动文件位置指针到指定位置
文件读写 read( ) 从指定文件读入数据,存入指定
区域
write( ) 把指定区域中的数据写到指定
文件中
例 student2 综合:成绩管理