内容正文:
第9章 文件
9.1 文件类型指针
9.2 文件的打开与关闭
9.3 文件的读写操作
9.4 文件定位与出错检测
9.5 其他文件函数
文件是存储在外部介质上数据的集合,是程序设计中一个重要的概念。操作系统以文件为单位对数据进行管理,也就是说,如果想找存储在外部介质上的数据,必须先按文件名找到所指定的文件,然后再从该文件中读取数据。要向外部介质上存储数据也必须先建立一个文件(以文件名为标识),才能向它输出数据。
9.1 文件类型指针
文件结构指针是一个指向文件有关信息的指针。这些信息定义了文件的文件名、状态和当前位置。在概念上文件结构指针标志着一个指定的磁盘文件。与文件结构指针组合的“流”用来告诉系统的每个缓冲型I/O函数应该到什么地方去完成操作。
文件结构指针是一个FILE型指针变量。FILE结构是在头文件<stdio.h>中定义的,因此对文件操作的程序,在最前面都应写上文件包含命令:
#include < 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; /*文件类型符*/
FILE结构体类型在头文件stdio.h中定义如下:
例如定义一个文件型指针变量:
FILE *fp;
说明:
fp就是一个指向FILE类型结构的指针变量,通过该文件指针变量就可以找到它相关联的文件,从而对文件进行读写操作。
9.2 文件的打开与关闭
在C语言程序中使用文件,需要完成以下工作:
(1)声明一个FILE类型的文件指针变量。
(2)通过调用fopen( )函数打开文件。
(3)调用适当的文件操作函数完成必要的I/O操作。
(4)通过调用fclose( )函数关闭文件。
简单说来,对磁盘文件的操作是
先打开,再读写,后关闭。
fopen( )函数打开一个流并把一个文件与这个流连接。fopen( )函数的调用形式为:
FILE * fp;
fp=fopen(filename,mode);
例:fp=fopen(“test.txt”,“w”)
其中:
filename必须是一个字符串组成的有效文件名;
mode是说明文件打开方式的字符串,在Dev-C++中,有效的 mode值如下表所示;
fopen( ) 函数如果成功地打开所指定的文件,则返回指向新打开文件的指针,且假定的文件位置指针指向文件首部;如果未能打开文件,则返回一个空指针。
1.fopen( )函数
文件操作方式 含 义 指定文件不存在时 指定文件存在时
“r”只读 打开一个文本文件只读 出错 正常打开
“w”只写 生成一个文本文件只写 建立新文件 原文件内容丢失
“a”追加 对一个文本文件添加 建立新文件 原文件尾部追加数据
“rb” 打开一个二进制文件只读 出错 正常打开
“wb” 生成一个二进制文件只写 建立新文件 原文件内容丢失
“ab” 对一个二进制文件添加 建立新文件 原文件尾部追加数据
“r+” 打开一个文本文件读/写 出错 正常打开
“w+” 生成一个文本文件读/写 建立新文件 原文件内容丢失
“a+” 打开或生成一个文本文件读/写 建立新文件 原文件尾部追加数据
“rb+” 打开一个二进制文件读/写 出错 正常打开
“wb+” 生成一个二进制文件读/写 建立新文件 原文件内容丢失
ab+” 打开或生成一个二进制文件读/写 建立新文件 原文件尾部追加数据
#include <stdio.h>
#include <stdlib.h>
int main()
{ FILE *fp;
if ((fp=fopen("test.txt","w"))==NULL)
{ puts(“不能打开此文件
”);
exit(1);
}
return 0;
}
说明:
在写文件之前先检验已打开的文件是否有错,如写保护或磁盘已写满等。上例中用了NULL,也就是0,因为没有文件指针会等于0。NULL是stdio.h 中定义的一个宏。
【例9.1】打开一个名为 test.txt 文件并准备写操作。
说明:
在打开一个文件作为读操作时,该文件必须存在;如果文件不存在,则返回一个出错信息。
以读操作“r”或“rb”方式打开一个文件,只能对该文件进行读出而不能对该文件进行写入。
用“w”或“wb”打开一个文件准备写操作时,如果该文件存在的话,则文件中原有的内容将被全部抹掉,并开始存放新内容;如果文件不存在,则建立这个文件。以写操作“w”或“wb”方式打开一个文件,只能对该文件进行写入而不能对该文件进行读出。
在“a”、 “ab”、 “a+”、 “ab+”方式打开一个文件,要在文件的尾部再加写些内容,则在打开文件时,如果该文件存在,则文件中原有的内容不会被抹掉,文件位置指针指向文件末尾;如果该文件不存在,就建立这个文件。
以“r+”或“rb+”方式打开一个文件进行读写操作时,该文件必须存在,如果文件不存在,则返回一个出错信息。
以“w+”或“wb+”方式打开一个文件进行读写操作时,如果该文件存在,则文件中原有的内容将被抹掉;如果该文件不存在,就建立这个文件。
fclose()函数用来关闭一个已由fopen()函数打开的流。
fclose()函数的调用形式为:
fclose(fp);
其中:
fp 是一个调用 fopen()时返回的文件指针。
若关闭文件成功,则fclose()函数返回值为0;若fclose()函数的返回值不为0,则说明出错。
2.fclose()函数
9.3 文件的读写操作
读文件:从磁盘文件中将数据先读入内存。
外存(文件)-->内存
写文件:先将数据从内存读出,写入文件中。
内存-->外存(文件)
(1) fputc()函数的调用形式为:
fputc(ch,fp);
功能:用来向一个已由fopen( )函数打开的写操作流中写一个字符。
若fputc()操作成功,则返回值是输出的字符;若操作失败,则返回EOF(文件结束)。
为了书写方便,在stdio.h已经定义了一个宏putc():
#define putc (ch,fp) fputc(ch,fp)
说明:putc() 与 fputc()可以作为相同的函数对待。
1.fputc() 函数、fgetc() 函数和feof()函数
(2) fgetc()函数的调用形式为:
fgetc(fp);
功能: fgetc()函数用来从一个已由fopen()函数打开的读操作流中读取一个字符。
fgetc()返回文件指针所指文件中当前位置上字符。当读到文件尾时,fgetc()返回一个EOF文件结束标记。
为了书写方便,在stdio.h已经定义了一个宏getc():
#define getc (fp) fgetc(fp)
说明:getc() 与 fgetc()可以作为相同的函数对待。
【例9.2】下面的程序段可以从文件头一直读到文件尾。
#include <stdio.h>
int main()
{ FILE *fp;
char ch;
fp=fopen("test.txt","r");
ch=fgetc(fp);
while(ch!=EOF){
printf("%c",ch);
ch=fgetc(fp);
}
return 0;
}
说明:
这只适用于读文本文件,不能用于读二进制文件。
(3) feof ()函数的调用形式为:
feof(fp);
功能: feof()函数是文件结束检测函数,可以判断在读二进制数据时文件是否真的结束。
feof()函数将返回一个整型值,在到达文件结束点时其值为1,未达到文件结束点时其值为0。
下面的语句可以从二进制文件首一直读到文件尾。
#include <stdio.h>
int main()
{ FILE *fp;
char ch;
fp=fopen("test.txt","r");
while(!feof(fp)) {
ch=getc(fp);
printf("%c",ch);
}
return 0;
}
说明:
这一语句对文本文件同样适用,即对任何类型文件都有效,所以建议使用本函数来判断文件是否结束。
int main(){
FILE *in,*out;
char infile[10]="d:\\a.txt",outfile[10]="d:\\b.txt",c;
if((in=fopen(infile,"r"))==NULL){
printf("文件打不开");
exit(0);
}
if((out=fopen(outfile,"w"))==NULL){
printf("文件打不开");
exit(0);
}
while(!feof(in))
{
c=fgetc(in);
fputc(c,out);
}
fclose(in);
fclose(out);
return 0;
}
【例9.3】将一个磁盘文件中的信息复制到另一个磁盘文件中,实现文件的拷贝功能。
fgets()函数和 fputs()函数用于从磁盘文件中读或写一个整型数据(一个字)。
用法与getc()函数和putc()函数完全相同,所不同的只是读写整形数据而不是字符。
【例9.4】下面的语句用来向文件指针fp所指的磁盘文件中当前位置上写一个整型数据。
putw(100,fp);
2.getw()函数和putw()函数
fgets()函数和 fputs()函数用来读写字符串的。
调用形式为:
fgets(str,length,fp);
和
fputs(str, fp);
其中:
str是一个字符指针或数组名;length是一个整数。函数fgets从fp指定文件的当前位置上读取字符串,直至读到换行符或第length-1个字符或遇到EOF为止。
3.fgets() 函数和fputs()函数
【例9.5】从指定文件读入一个字符串。
fgets(str,100,fp);
向指定的文件输出一个字符串。
fputs("guan-zhi@163.com",fp);
fread()函数 和fwrite()函数是用来读写数据块的函数。
调用形式为:
fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);
其中:
buffer:是一个指针,在fread函数中,它表示存放输入数据的首地址。在fwrite函数中,它表示存放输出数据的首地址。
size:表示数据块的字节数。
count:表示要读写的数据块块数。
fp: 表示文件指针。
4.fread() 函数和fwrite() 函数
fread()函数操作成功时,返回实际读取的字段个数count;到达文件尾或出现错误时,返回值小于count。fwrite()函数操作成功时,返回实际所写的字段个数count;返回值小于count,说明发生了错误。
【例9.6】如果文件以二进制文件方式打开,可以用fread()和fwrite()读写任何类型信息。
fread(f,4,2,fp);
表示:从fp所指向的文件中读取2个4个字节的数据,存储到数组f中。
或
fwrite(f,4,2,fp);
表示:从数组f所指的数据(2个4字节)写入fp所指的文件。
例如:
#include <stdio.h>
main()
{
FILE *fp;
int i,a[10]={1,2,3,4,5,100,100,100,100,100};
fp=fopen("d2.dat", "wb");
fwrite(a,sizeof(int),5,fp);
fwrite(a,sizeof(int),5,fp);
fclose(fp);
fp=fopen("d2.dat", "rb");
fread(a,sizeof(int),10,fp);
fclose(fp);
for(i=0;i<10;i++)
printf("%d",a[i]);
}
思考:
a改为a+5呢?
这两个函数功能是与printf()和scanf()完全相同,但其操作对象是磁盘文件。
调用方式为:
fprintf(fp,“控制字符串”,参数表);
fscanf(fp,“控制字符串”,参数表);
其中:
fp是一个有效的文件指针,控制字符串和参数表同printf()和scanf()函数。
fprintf()函数操作成功,返回实际被写的字符个数;不成功返回一个负数。
fscanf()函数操作成功,返回实际被赋值的参数个数;若返回EOF,则表示试图去读取超过文件末尾的部分。
5.fprintf() 函数和fscanf()函数
fprintf()函数操作成功,返回实际被写的字符个数;出现错误时,返回一个负数。fscanf()函数操作成功,返回实际被赋值的参数个数;若返回EOF,则表示试图去读取超过文件末尾的部分。
【例9.7】按格式实现文件fp与变量i、t之间的输出/输入操作。
fprintf(fp, "%d,%6.2f",i,t);
或
fscanf(fp, "%d,%f",&i,&t);
注意:
虽然fprintf()函数和fscanf()函数是向磁盘文件读写各种数据最容易的方法,但效率并不一定最高。因为它们以格式化的ASCII数据而不是二进制数据进行输入输出,与在屏幕上显示是相同的。如果要求速度快或文件很长时应使用fread()函数和 fwrite()函数。
例如:
#include <stdio.h>
int main()
{
FILE *fp;
int k,n,a[6]={1,2,3,4,5,6};
fp=fopen("d2.dat","w");
fprintf(fp,"%d%d%d
",a[0],a[1],a[2]);
fprintf(fp,"%d%d%d
",a[3],a[4],a[5]);
fclose(fp);
fp=fopen("d2.dat","r");
fscanf(fp,"%d%d",&k,&n);
printf("%d %d
",k,n);
fclose(fp);
return 0;
}
9.4 文件定位与出错检测
1、文件定位函数——fseek()函数
调用形式为:
fseek(文件指针,位移量,起始点);
功能:fseek()函数可以完成随机读写操作,可以随机设置文件位置指针。
说明:
fseek()函数操作成功,返回0,返回非零值表示失败。
“位移量”表示移动的字节数,要求位移量是long型数据,以便在文件长度大于64KB 时不会出错。当用常量表示位移量时,要求加后缀“L”。
“起始点”是下表中所示的几个宏名之一:
宏名字 数值表示 起始点
SEEK_SET 0 文件开始为起点
SEEK_CUR 1 文件当前位置为起点
SEEK_END 2 文件末尾为起点
以下是fseek函数调用的几个例子:
fseek(fp,100L,0); 将位置指针移到离文件头100个字节处
fseek(fp,50L,1); 将位置指针移到离当前位置50个字节处
fseek(fp,-10L,2); 将位置指针从文件末尾处向后退10个字节。
ferror()函数用来确定文件操作中是否出错。
调用形式为:
ferror(fp);
其中:fp是一个调用fopen()时返回的文件指针。若在文件操作中发生了错误,则ferror()函数返回一个非零值,即“真”;否则返回值为0,即“假”。
2、出错检测函数——ferror()函数
9.5 其他文件函数
1、rewind()函数
rewind()函数用来将文件位置指针重新设置到该文件首。
其调用形式是:
rewind(fp);
其中fp是一个有效的文件指针,其定义如前所述。
此函数没有返回值,但使feof()函数的值置为0。
此函数等效于fseek(fp,0L,SEEK_SET);
ftell()函数的作用是得到流式文件中位置指针的当前位置,用相对于文件开头的位移量来表示。
其调用形式是:
ftell(fp);
其中fp是一个有效的文件指针,其定义如前所述。
2、ftell()函数
由于文件中的位置指针经常移动,人们往往不容易辨清其当前位置。用ftell()函数可以得到当前位置。如果ftell()函数返回值为-1L,表示出错。
例如:
i=ftell(fp);
if (i==-1L) printf("出错
");
变量i存放当前位置,应当定义为长整型变量,如调用函数出错(如不存在这个文件),则输出“出错”信息。
clearerr()函数的作用是使文件错误标志和文件结束标志置为0。
其调用形式是:
clearerr(fp);
其中fp是一个有效的文件指针,其定义如前所述。
3、clearerr()函数
假设在调用一个输入输出函数时出现错误,ferror()函数值为一个非零值。在调用clearerr(fp)后,ferror(fp)的值变成0。
只要出现错误标志,就一直保留,直到对同一文件调用clearerr()函数或rewind()函数,或任何其他一个输入输出函数为止。
remove( )函数的作用是删除所指定的文件。
其调用形式是:
remove(filename);
字符串filename是指定要删除的文件名。该函数正确执行完毕返回0,否则返回非零值。
4、remove( )函数
本章小结
文件是程序设计中一种重要的数据类型,是存储在外部介质上的一组数据集合。C语言中文件根据数据组织形式有二进制文件和文本文件。
对文件操作分为三步:打开文件、读写文件、关闭文件。
文件的读写操作可以使用库函数fscanf() 与fprintf()函数、fgetc()与fputc()函数、fgets()与fputs()函数、fread()与fwrite()函数,这些函数最好配对使用,以免引起输入输出的混乱。
$$