第十章 文 件
第十章 文 件
10.1 文件
10.2 缓冲文件系统基础
第十章 文 件
10.1 文 件
10.1.1 文件的概念
磁盘文件在 DOS管理中被定义为存贮在外部介质上的程序
或数据的集合, 是一批逻辑上有联系的数据每个文件都有一
个文件名作为标识, 每个文件在磁盘中的具体存放位置, 格式
都由操作系统中的文件系统管理, 也就是说, 操作系统是以
文件为单位对程序或数据进行管理的 。 编辑后存于磁盘上的源
程序文件 *.C,经编译后得到的目标文件 *.OBJ,连接之后形成
的可执行文件 *.EXE等 。
第十章 文 件
在 C语言中文件的含义更为广泛, 不仅包含以上所述的
磁盘文件, 还包括一切能进行输入 /输出的终端设备, 它们被
看成是设备文件 。 如键盘常称为标准输入文件, 显示器称为
标准输出文件 。
文件是由磁盘文件和设备文件组成的 。 作为磁盘文件之
一的数据文件是本章学习的主要对象 。 数据文件可以看作是
C中最后一种数据类型, 是 C语言重要的组成部分 。
根据文件内数据的组织形式, 文件可分为文本 ( text)
文件和二进制文件 。 文本文件又称为 ASCII码文件, 它的每
一个字节存放一个字符的 ASCII码 。
第十章 文 件
10.1.2 数据流
数据流是对数据输入输出行为的一种抽象 。 各种各样的终
端设备或磁盘文件的细节是非常复杂多样的直接对它们编程将
会非常繁琐 。 引入数据流的概念有效地解决了这一难题 。 只要
建立了输入输出数据流, 编程者在应用程序中就不需要关心底
层输入输出设备或是任何磁盘文件的具体细节差异 。 程序中要
输入数据, 只需从输入数据流中读入;输出数据只需向输出数
据流中写出即可, 这样就使程序完全与具体硬件资源脱离了关
系, 也就是说数据流使 C程序与具体系统完全不相关, 使 C程
序可以非常方便地移植 。
第十章 文 件
10.1.3 C的文件系统及其与流的关系
C的文件系统可分为缓冲文件系统和非缓冲文件系统两
类 。 所谓缓冲文件系统, 又称高级磁盘输入输出系统 。 在调
用这种文件处理函数时, 会自动在用户内存区中为每一个正
在使用的文件划出一片存贮单元, 称为开辟一个缓冲区 。
设立缓冲区的原因是磁盘的读写速度比内存的处理速度
要慢很多, 而且磁盘驱动器是机电设备, 定位精度比较差,
所以磁盘数据存取要以扇区 ( 磁盘上某磁道中的一个弧形段,
通常存放固定数量的数据 ) 或者簇 ( 由若干扇区组成 ) 为单
位 。
第十章 文 件
10.2 缓冲文件系统基础
一般缓冲文件操作有三个必需的步骤,
(1) 在使用文件前要调用打开函数将文件打开, 若打开
失败, 则返回一个空指针;若打开正常, 可以得到一个文件
指针, 并利用它继续对文件操作 。
(2) 可调用各种有关函数, 利用该指针对文件进行具体
处理, 一般要对文件进行读或写操作 。
(3) 在文件用完时, 应及时调用关闭函数来关闭文件,
切断数据流, 防止数据遗失或误操作破坏文件内容 。
第十章 文 件
10.2.1 文件指针
文件类型 FILE不是 C语言的新类型, 它是用 typedef定义
出来的有关文件信息的一种结构体类型 。 如 Turbo C 2.0版的
stdio.h文件中有如下的定义,
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.2.2 打开文件( fopen函数)
打开函数 fopen() 的调用方式是,
FILE *fp;
fp=fopen(文件名,使用文件方式) ;
fp=fopen("A1.DAT","r");
第十章 文 件
表 12.1 文件使用方式
第十章 文 件
10.2.3 关闭文件 (fclose函数 )
在使用完一个文件后应该调用 fclose函数关闭文件 。
fclose函数的调用格式为 fclose( 文件指针 ) 。 例如
fclose(fp);就把指针 fp所指的文件关闭了, 也就是断开了
打开文件时建立的数据流 ——fp与具体文件的联系, 即不能
再通过 fp对某个具体文件进行操作 。
第十章 文 件
如果在程序终止之前不关闭文件, 将可能丢失缓冲
区中最后一批未处理的数据, 因为 fclose函数的调用不仅
释放文件指针, 还刷新缓冲区 。 fclose函数将缓冲区中可
能遗留的未装满送走的数据输入内存或输出至磁盘文件,
以确保数据不丢失 。 当然, 程序结束时会自动关闭文件,
但用完文件后及时关闭是一个好的编程习惯 。
fclose函数也返回一个值,0表示顺利返回,非 0表示
关闭错误。
第十章 文 件
10.2.4 文件的读写
1,fputc函数和 fgetc
fputc
fputc(ch,fp);
该函数的作用是将字符 ( ch的值 ) 输出到 fp所指向的文
件上去 。 其中 ch是要输出的字符, 它可以是一个字符常量,
也可以是一个字符变量 。 fp是文件指针, 它是从 fopen函数得
到的返回值 。 fputc函数也带回一个值, 如果输出成功则返回
值就是输出的字符;如果输出失败, 则返回一个 EOF。 EOF
是在 stdio.h文件中定义的符号常量, 值为 -1。
第十章 文 件
fgetc
ch=fgetc(fp);
该函数的作用是从指定文件读入一个字符, 该文件必
须是以读或读写方式打开的 。 其中 fp为文件型指针, 指向
所打开备读的文件; ch为字符变量, 接收 fgetc函数带回的
字符如果在执行 fgetc读字符时遇到文件结束符, 函数则返
回一个文件结束标志 EOF,可以利用它来判断是否读完了文
件中的数据 。 如想从一个磁盘文件顺序读入字符并在屏幕
上显示出来, 可编程为
while((ch=fgetc(fp))![KG-*4]=EOF)
putchar(ch);
第十章 文 件
EOF不是可输出字符, 因此在屏幕上显示不出来 。 由于
字符的 ASCII码不可能出现 -l,因此 EOF定义为 -l 是合适的 。
当读入的字符值等于 -1( 即 EOF) 时, 表示读入的已不是正
常的字符而是文件结束符 。 但以上只适用于读文本文件 。 现
在标准 C已允许用缓冲文件系统处理二进制文件, 而读入某
一个字节中的二进制数据的值有可能是 -1,而这又恰好是
EOF的值 。 这就出现了读入有用数据却被处理为, 文件结束,
的情况, 即终止符设置不恰当 。 为了解决这个问题, 标准 C
提供了一个 feof()函数来判断文件是否真的结束 。 feof(fp)用来
测试 fp所指向的文件当前状态是否为, 文件结束,, 如果是
文件结束, 函数 feof(fp) 的值为 1(真 ),否则为 0(假 )。
第十章 文 件
例如,顺序读入一个二进制文件中的数据的程序段如下,
while(!feof(fp))
{ c=fgetc(fp);
……
}
当未遇文件结束时, feof(fp)的值为 0,!feof(fp)为 l,读入一
个字节的数据赋给变量 c( 接着可做其它处理 ), 之后再求
feof(fp) 函数, 循环工作直到文件结束, feof(fp) 值变为
1,!feof(fp) 值为 0,结束 while循环 。 这种方法也适用于文
本文件 。
第十章 文 件
例 10.1 建立一个磁盘文件, 将键入的回车前的若干个
字符逐个写入该文件 。
# include <stdio.h>
main( )
{ FILE *fp;
char ch,filename[ 13] ;
printf("\nInput the file\′s name,");
gets(filename); /* 注 1 */
if((fp=fopen(filename,"w"))==NULL)
{printf("Can not open the file\n");
exit(0);
}
第十章 文 件
printf("Input the characters to the file,\n");
while((ch=getchar( )) ! = ′\n′) /* 注 2 */
{ fputc(ch,fp); /* 注 3 */
putchar(ch); /* 注 4 */
}
fclose(fp);
}
第十章 文 件
运行情况如下,
Input the file′s name,fileex1.dat ( 注 1要求的输入磁盘文件名 )
Input the characters to the file,
What inside the file ( 注 2 要求的键入一个字符串 )
What inside the file ( 注 4输出到显示器上的字符串, 与写入文件的
内容一样, 以资核对 )
程序运行之后, 可以查看文件目录, 将多出一个名为
fileex1.dat的数据文件, 可用 DOS命令将其内容打印出来,
type fileex1.dat
What inside the file (由注 3行循环写入的)
第十章 文 件
2,fgets函数和 fputs
fgets的作用是从指定文件读入一个字符串 。 如,
fgets(str,n,fp);
从 fp指向的文件读入 n-1个字符, 并把它们放到字符数
组 str中, 如果在读入 n-1个字符结束之前遇到换行符或 EOF,
读入即结束 。 字符串读入在最后加一个 ‘ \0’字符,fgets函数
返回值为 str的首地址 。
fputs函数的作用是向指定的文件输出一个字符串,如,
fputs(" China",fp);
第十章 文 件
3,fprinf函数和 fscanf
fprintf函数, fscanf函数与 printf和 scanf函数作用类似,
都是格式化读写函数 。 前二者的读写对象是磁盘文件, 而
后二者是终端设备 。 所以前二者函数调用参数中要多出一
代表文件的文件指针 。 一般调用方式为
fprintf( 文件指针, 控制字符串, 参量表 ) ;
fscanf(文件指针,控制字符串,参量表) ;
第十章 文 件
例 10.2 按格式键入字符型, 整型, 实型各一数, 写入
文件 dform.dat,再读出送显 。
# include<stdio.h>
main()
{ int i,i1;
char ch,ch1;
float f,f1;
FILE *fp;
printf("\nInput ch i f,");
scanf("%c %d %f",&ch,&i,&f);
第十章 文 件
if((fp=fopen("dform.dat","w"))==NULL) /* 注 1*/
{printf("Can not open the file\n");
exit(0);
}
fprintf(fp,"%c %5d %4.1f",ch,i,f); /* 注 2 */
fclose(fp); /* 注 3 */
if((fp=fopen("dform.dat","r"))==NULL)
{printf("Can not open the file\n");
exit(0);
}
fscanf(fp,"%c %d %f",&ch1,&i1,&f1);
printf("%c %5d %4.1f",ch1,i1,f1);
fclose(fp);
}
第十章 文 件
程序运行情况,
Input ch i f,a 2 2.2
a 2 2.2
dform.dat中的内容同屏显。
第十章 文 件
4,fread函数和 fwrite函数
它们的一般调用形式为
fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);
其中, buffer 是一个地址 。 对 fread来说, 它是读入数据将要
存放处的地址 。 对 fwrite来说, 是要输出数据的地址 ( 以上指
的是起始地址 ) 。
size是要读写的一个数据块的字节数 。
count是要进行读写数据块的个数 。
fp是文件指针,指向待读或写的文件。
第十章 文 件
例 10.3 建立一个有关工人工资的数据文件。
# include<stdio.h>
# define SIZE 6
struct staff
{ char name[ 10] ;
int salary;
int cost;
} worker[ SIZE] ;
void savef()
{ FILE *fp;
int i;
if((fp=fopen("work.dat","wb"))==NULL)
第十章 文 件
{printf("Can not open the file\n");
return;
}
for(i=0; i<SIZE; i++)
if(fwrite(&worker[ i],sizeof(struct staff),1,fp)![KG-*4]=1)
printf("File write error\n");
fclose(fp);
}
main()
{ int i;
printf("\nInput %d worker\′s name salary cost,\n",SIZE);
for(i=0; i<SIZE; i++)
scanf("%s%d%d",worker[ i],name,&worker[ i],salary,
&worker[ i],cost);
savef();
}
第十章 文 件
10.2.5 文件的定位
1,rewind
rewind函数可以强制使当前工作指针指向文件的开头 。
一般在要重新从头读写文件时使用 。 如下例, 在读了文件
dfr.dat一遍送显示器后, 文件的位置指针已移到文件的最后,
为了重新读一遍再写到文件 dfw.dat中, 必须先执行一次
rewind函数, 才能正确读出 。
第十章 文 件
例 10.4 将已建好的文件 dfr.dat的内容顺序读一遍送显示
器, 再读一遍复制到文件 dfw.dat中 。
# include<stdio.h>
main( )
{ int i;
char ch;
float f,f1;
FILE *fp1,*fp2;
if((fp1=fopen("dfr.dat","r"))==NULL)
{ printf("Can not open the file for reading\n");
exit(0);
}
第十章 文 件
if((fp2=fopen("dfw.dat","w"))==NULL)
{printf("Can not open the file for writing\n");
exit( 0);
}
fscanf(fp1,"%c %d %f",&ch,&i,&f);
printf("%c,%5d,%4.1f\n",ch,i,f);
rewind(fp1);
fscanf(fp1,"%c %d %f",&ch,&i,&f1);
fprintf(fp2,"%c %d %f",ch,i,f1);
fclose(fp1);
fclose(fp2);
}
第十章 文 件
2,fseek函数
利用 fseek函数可以控制文件位置的指针进行随机读写 。
fseek函数的调用形式为 fseek( 文件类型指针, 位移
量, 起始点 ) ;
起始点用 0,1 或 2 代表,0——文件的开始, 1——当前位
置, 2——文件末尾;
位移量指从起始点向前移动的字节数;
fseek函数一般用于二进制文件, 因为文本文件要发
生字符转换, 计算位置时容易发生混乱 。
第十章 文 件
例 10.5 将例 10.3形成的职工数据文件中的第 1,3,5个
工人的信息读出, 送显 。
# include<stdio.h>
# define SIZE 6
struct staff
{ char name[ 10] ;
int salary;
int cost;
} worker[ SIZE] ;
第十章 文 件
main()
{ FILE *fp;
int i;
if((fp=fopen("work.dat","rb"))==NULL)
{printf("Can not open the file\n");
exit(0);
}
for(i=0; i<SIZE; i++,i++)
{fseek(fp,i*sizeof(struct staff),0);
fread(&worker[ i],sizeof(struct staff),1,fp);
printf(" %s %d %d\n",worker[ i],name,worker[ i],salary,
worker[ i],cost);
}
fclose(fp);
}
第十章 文 件
若形成 work.dat文件时的输入数据为
Li1 1100 100
Li2 1200 200
Li3 1300 300
Li4 1400 400
Li5 1500 500
Li6 1600 600
Li1 1100 100
Li3 1300 300
Li5 1500 500
第十章 文 件
3,ftell
ftell函数的作用是得到流式文件中位置指针的当前位置,
用相对于文件开头的位移量来表示 。
由于文件的位置指针经常移动, 往往不易搞清其当前
位置, 用 ftell()函数可以返回其当前位置, 若返回 -1L,表
示函数调用出错 。 例如,
i=ftell( fp) ;
if( i==-1L) printf( "error\n") ; 。