第一部分 文件的打开和关闭
1.1文件指针
在C语言中用一个指针变量指向一个文件,这个指针成为文件指针。通过文件指针,可以对它所指的文件进行各种操作。
定义文件指针的一般格式为:
FILE * 指针变量标识符;
其中FILE应为大写,它实际上是由系统定义的一个结构,该结构中含有文件名、文件状态和文件当前位置等信息。在编写源程序时不必关系FILE结构的细节。
如:
FILE * fp;
表示fp是指向FILE结构的指针变量,通过fp即可查找存放某个文件信息的结构变量,然后按结构变量提供的信息找到该文件,实施对文件的操作。习惯上把fp称为指向一个文件的指针。
1.2文件的打开与关闭具体操作
文件在进行读写操作之前要先打开,使用完毕要关闭。所为打开文件,实际上是建立文件的各种有关信息,并使文件指针指向该文件,以便进行各种其他操作。关闭文件则断开指针与文件之间的联系,也就是禁止再对该文件进行操作。
A.文件的打开(fopen()函数)
fopen()函数用来打开一个文件,其调用的一边形式为:
文件指针名=fopen(文件名,使用文件方式)
其中:
(1)“文件指针名”必须是被说明为FILE类型的指针变量。
(2)“文件名”是被打开文件的文件名,是字符串变量或字符串数组。
(3)“使用文件方式”是指文件的类型和操作要求。
如:
FILE * fp;
fp=("file a","r");
其意义是在当前目录下打开文件file a,但只允许对文件进行“读”操作,并使fp指向该文件。
文件的使用方式共有12中,如下表:
文件使用方式 | 意义 |
“rt” | 以只读形式打开一个文本文件,只允许读数据 |
“wt” | 以只读形式打开或建立一个文本文件,只允许读数据 |
“at” | 以追加形式打开一个文本文件,并在文件末尾写数据 |
“rb” | 以只读形式打开一个二进制文件,只允许读数据 |
“wb” | 以只读形式打开或建立一个二进制文件,只允许读数据 |
“ab” | 以追加形式打开一个二进制文件,并在文件末尾写数据 |
“rt+” | 以读写形式打开一个文本文件,允许读写 |
“wt+” | 以读写形式打开或建立一个文本文件,允许读写 |
“at+” | 以读写形式打开一个文本文件,允许读,或在文件末尾追加数据 |
“rb+” | 以读写形式打开一个二进制文件,允许读写 |
“wb+” | 以读写形式打开或建立一个二进制文件,允许读写 |
“ab+” | 以读写形式打开一个二进制文件,允许读,或在文件末尾追加数据 |
对于文件使用方式有以下几点说明:
(1)文件使用方式由r、w、a、t、b和+6个字符组成。其中t为文本文件,可省略不写。
(2)凡用“r”方式打开一个文件,进行读操作时,该文件必须存在,且只能从该文件读出数据。
(3)用“w”方式打开文件,进行写操作时,只能向文件写入数据。若打开的文件不存在,则以指定的文件名建立新文件,若打开的文件已经存在,则将该文件删除,重新建立一个新文件。
(4)若要向一个已存在的文件追加新信息,只能用“a”方式打开文件。但此时该文件必须是存在的,否则将会出错。
(5)在打开一个文件时,如果出错,fopen函数将返回一个空指针NULL。在程序中可以用这一信息来判断是否完成打开文件的操作,并作相应的处理。
(6)打开文件常用程序段
if(fp=fopen("c://CYYXX","rb")==NULL)
{printf("/nerror on open c://CYYXX file!"); getch(); exit(1);}
这段程序的意义是,如果返回的指针为空“NULL”,表示不能打开C盘根目录下的CYYXX文件,则给出提示信息error on open c://CYYXX file!,下一行getch()的功能是当用户按下键盘上的任意键时,程序才继续执行,因此用户可利用这个等待时间阅读出错提示。按任意键后执行exit(1)退出程序。
(7)把一个文本文件读入内存时,要将ASCII码转换成二进制码,而要把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,因此对文本文件的读写操作要花费较多的转换时间。对二进制文件执行读写操作时不存在这种转换。
B.文件关闭函数(fclose()函数)
文件一旦使用完毕,可用关闭文件函数把指定文件关闭,以避免文件数据的丢失等错误的发生。
fclose()函数调用的一般形式为:
fclose(文件指针);
如:
fclose(fp);
正常完成关闭文件操作时,fclose函数返回值为0。如返回值为非零值则表示有错误发生。
例1:判断stu.txt文件能否以只读方式打开,并返回相应的提示信息。
#include <stdio.h>
#include<string.h>
main(){
FILE *fp;
fp=fopen("D://充实的//Visual C 工作目录//stu.txt","r");
if(fp==NULL)
printf("File cannot be open/n");
else
{
printf("File can be open/n");
fclose(fp);
}
}
例2:打开和关闭一个可读可写的二进制文件。
#include <stdio.h>
#include<string.h>
main(){
FILE *fp;
fp=fopen("D://充实的//Visual C 工作目录//test.dat","rb+");
if(fp==NULL)
{
printf("File cannot be open/n");
exit(1);
}
else
printf("file can be open/n");
if(fclose(fp))
printf("file close error/n");
}
第二部分 文件的读写
对文件的读和写是最常用的文件操作。在C语言中提供了多种文件读写的函数。
(1)字符读写函数:fgetc和fputc。
(2)字符串读写函数:fgets和fputs。
(3)数据块读写函数:fread和fwrite。
(4)格式化读写函数:fscanf和fprintf。
2.1字符读写函数fgetc和fputc
字符读写函数是以字符(字节)为单位的读写函数。每次可从文件读取或向文件写入一个字符。
A.读字符函数fgetc()
fgetc函数的功能是从指定的文件中读一个字符,函数调用的形式为:
字符变量=fgetc(文件指针);
如:
ch=fgetc(fp);
其意思是从打开的文件fp中读取一个字符并送入ch中。
对于fgetc()函数的使用方法,有以下几点说明:
(1)在fgetc()函数调用中,读取的文件必须是以读(r)或读写(r+)方式打开的。
(2)读取字符后,也可以不向字符变量赋值。
如:
fgetc(fp);
但读出的字符不能保存。
(3)在文件内部有一个位置指针。用来指向文件的当前读取字节。在文件打开时,该指针总是指向文件的第一个字节。使用fgetc函数后,该位置指针将向后移动一个字节。因此可以连续多次使用fgetc函数,读取多个字符。应注意文件指针和文件内部的位置指针不是一回事。文件指针是指向整个文件的,需在程序中说明,只要不重复赋值,文件指针的值是不变的。文件内部的位置指针用以指示文件内部的当前读写位置,每读写一次,该指针向后移动一个字节,它不需在程序中定义说明,有系统自动设置。
例3:读取文件c1.txt中的数据,并在屏幕上输出。
#include <stdio.h>
#include<string.h>
main(){
FILE *fp;
char ch;
fp=fopen("D://充实的//Visual C 工作目录//c1.txt","rb+");
if(fp==NULL)
{
printf("File cannot be open/n");
exit(1);
}
ch=fgetc(fp);
while(ch!=EOF)
{
putchar(ch);//putchar函数(字符输出函数)的作用是向终端输出一个字符。
ch=fgetc(fp);
}
fclose(fp);
}
只要读出的字符不是文件结束标志(每个文件末都有一结束标志EOF)就把字符显示在屏幕上,再读入下一个字符。每读一次,文件内部的位置指针向后移动一个字节,文件结束时,该指针指向EOF。执行本程序将显示整个文件。
B.写字符函数fputc()
fputc函数的功能是把一个字符写入指定的文件中,函数调用的形式为:
fputc(字符量,文件指针);
其中,待写入的字符量可以是字符常量或变量,如:
fputc('a',fp);
其意义是把字符a写入fp所指向的文件中。
对于fputc()函数的使用,有以下几点说明:
(1)被写入的文件可以用写、读写或追加的方式打开,用写或读写方式打开一个已存在文件时将清除原有文件内容,写入字符从文件头开始。如需保留原有文件内容,希望写入的字符从文件末尾开始存放,必须以追加方式打开文件。被写入的文件若不存在,则创建该文件。
(2)每写入一个字符,文件内部位置指针向后移动一个字节。
(3)fputc()函数有一个返回值,如写入成功则返回写入的字符,否则返回EOF。可以由此来判断写入是否成功。
例4:通过键盘输入一行字符,写入一个文件(string),再把该文件内容读出,并显示在屏幕上。
#include <stdio.h>
#include<string.h>
main(){
FILE * fp;
char ch;
if((fp=fopen("D://充实的//Visual C 工作目录//string.txt","wt+"))==NULL)
{
printf("file cannot open");
exit(0);
}
printf("please input a string:/n");
ch=getchar();
while(ch!='/n')
{
fputc(ch,fp);
ch=getchar();
}
rewind(fp);
ch=fgetc(fp);
while(ch!=EOF)
{
putchar(ch);
ch=fgetc(fp);
}
printf("/n");
fclose(fp);
}
rewind()函数用于把fp所指文件的内容位置指针移到文件头。
2.2 字符串读写函数fgets()和fputs()
A.字符串读写函数fgets()和fputs()
函数的功能是指从指定的文件中读取一个字符串到字符数组中,函数调用的形式为:
fgets(字符数组名,n,文件指针);
其中n是一个正整数。表示从文件中读出的字符串不超过n-1个字符串。在读取最后一个字符后加上串结束标志'/0'。
如:
fgets(str,n,fp);
意思是从fp所指的文件中读取n-1个字符,送入字符数组str中。
例5:从string文件中读取一个包含10个字符的字符串。
#include <stdio.h>
#include<string.h>
main(){
char str[11];
FILE * fp;
if((fp=fopen("D://充实的//Visual C 工作目录//string.txt","rt"))==NULL)
{
printf("file cannot open!/n");
getch();//用getch();会等待你按下任意键,再继续执行下面的语句;
exit(0);//正常退出;exit(1)异常退出
}
fgets(str,11,fp);
printf("/n%s/n",str);
fclose(fp);
}
本例定义了一个字符数组str,长度为11个字节,在以读文本文件方式打开文件string后,从中读取10个字符,送入str数组中,在数组最后一个单元内将加上'/0',然后在屏幕上显示输出str数组。
对fgets函数有两点说明:
(1)在读出n-1个字符之前,如遇到了换行符或EOF,则读操作结束。
(2)fgets函数也有返回值,其返回值是字符数组的首地址。
B.写字符串fputs()
fputs函数的功能是向指定的文件写入一个字符串,其调用形式为:
fputs(字符串,文件指针);
其中字符串可以是字符串常量,也可以是字符串数组名,或指针变量,如:fputs("abcd",fp);
其意思是把字符串"abcd"写入fp所指的文件之中。
例6:在string文件中追加一个字符串。
#include <stdio.h>
#include<string.h>
main(){
char str[20],ch;
FILE * fp;
if((fp=fopen("D://充实的//Visual C 工作目录//string.txt","at+"))==NULL)
{
printf("file cannot open!/n");
getch();//用getch();会等待你按下任意键,再继续执行下面的语句;
exit(1);//正常退出;exit(1)异常退出
}
printf("please input a string!/n");
scanf("%s",str);
fputs(str,fp);
rewind(fp);
ch=fgetc(fp);
while(ch!=EOF){
putchar(ch);
ch=fgetc(fp);
}
printf("/n");
fclose(fp);
}
例7:从一个文本文件test1.txt中读出字符串,在写入另一个文件test2.txt中。
#include <stdio.h>
#include<string.h>
main(){
char str[128];
FILE * fp1,*fp2;
if((fp1=fopen("D://充实的//Visual C 工作目录//test1.txt","r"))==NULL)
{
printf("file cannot open!/n");
getch();//用getch();会等待你按下任意键,再继续执行下面的语句;
exit(1);//正常退出;exit(1)异常退出
}
if((fp2=fopen("D://充实的//Visual C 工作目录//test2.txt","w"))==NULL)
{
printf("file cannot open!/n");
getch();//用getch();会等待你按下任意键,再继续执行下面的语句;
exit(1);//正常退出;exit(1)异常退出
}
if(strlen(fgets(str,128,fp1))>0)//从文件中读回的字符串长度大于0
{
fputs(str,fp2);//从文件1读字符串,并写到文件2中
printf("%s",str);//在屏幕上显示
}
fclose(fp1);
fclose(fp2);
}
2.3 数据块读写函数fread()和fwrite()
C语言还提供了整块数据的读写函数。可用来读写一组数据,如一个数组元素,一个结构变量的值等。
读数据块函数调用的一般形式为:
fread(buffer,size,count,fp);
写数据块函数调用的一般格式为:
fwrite(buffer,size,count,fp);
其中:
(1)buffer是一个指针,在fread函数中,它表示存放输入数据的首地址。在fwrite函数中,它表示存放输入数据的首地址。
(2)size表示数据块的字节数。
(3)count表示要读写的数据块块数。
(4)fp表示文件指针。
如:
fread(fa,4,5,fp);
其意思是从fp所指的文件中,每次读取4个字节(一个实数),送入实数组fa中,连续读5次,即读取5个实数到fa中。
例8:通过键盘输入两个学生数据,写入一个文件中,再读出这两个学生的数据,显示在屏幕上。
#include <stdio.h>
#include<string.h>
struct stu{
char name[10];
int num;
int age;
char addr[15];
}boya[2],boyb[2],*pp,*qq;
main(){
FILE *fp;
int i;
pp=boya;
qq=boyb;
if((fp=fopen("D://充实的//Visual C 工作目录//test1.txt","wt+"))==NULL)
{
printf("file cannot open!/n");
getch();
exit(1);
}
printf("/nplease input data!/n");
for(i=0;i<2;i++,pp++)
scanf("%s%d%d%s",pp->name,&pp->num,&pp->age,pp->addr);//向boya数组中输入数据,在scanf参数的最后不要加/n
pp=boya;
fwrite(pp,sizeof(struct stu),2,fp);
rewind(fp);
fread(qq,sizeof(struct stu),2,fp);
i=0;
printf("/nname/tnum age addr/n");
for(i=0;i<2;i++,qq++)
printf("%s/t%5d%7d %s/n",qq->name,qq->num,qq->age,qq->addr);
fclose(fp);
}
本例程序定义了一个结构体stu,说明了两个结构体数组boya和boyb及两个结构指针变量pp和qq。pp指向boya,qq指向boyb。
2。4 格式化读写函数fscanf()和fprintf()
fscanf()函数,fprintf()函数与前面是用的scanf()和printf()函数的功能相似,都是格式化读写函数。两者的区别在于fscanf()函数和fprintf()函数的读写对象不是键盘和显示器,而是磁盘文件。
fscanf和fprintf函数的调用格式为:
fsccanf(文件指针,格式字符串,输入列表);
fprintf(文件指针,格式字符串,输入列表);
如:
fscanf(fp,"%d%s",&i,s);
fprintf(fp,"%d%c",j,ch);
例9:将一些格式化的数据写入文本文件,再从该文件中以格式化方法读出显示到屏幕上,其格式化数据为两个学生记录,包括姓名、学号和两科成绩。
#include <stdio.h>
#include<string.h>
struct stu{//定义结构体
char name[10];
char num[10];
float score1;
float score2;
}student;
main(){
FILE *fp;//定义指针
int i;
if((fp=fopen("D://充实的//Visual C 工作目录//test1.txt","wt"))==NULL)//判断能否打开文件
{
printf("file cannot open!/n");
exit(0);
}
printf("/nplease input data!/n");//输入文件内容
for(i=0;i<2;i++)
{
scanf("%s%s%f%f",student.name,student.num,&student.score1,&student.score2);
//通过键盘输入
fprintf(fp,"%s %s%7.2f%7.2f/n",student.name,student.num,student.score1,student.score2);
//写入文件
}
fclose(fp);//关闭文件
if((fp=fopen("D://充实的//Visual C 工作目录//test1.txt","rt"))==NULL)
{
printf("file cannot open!/n");
getch();
exit(1);
}
while(fscanf(fp,"%s%s%f%f/n",student.name,student.num,&student.score1,&student.score2)!=EOF)//从文件读入
{
printf("%s %s% 7.2f%7.2f/n",student.name,student.num,student.score1,student.score2);//显示到屏幕上
}
fclose(fp);
}
第三部分 文件的随机读写
随机读写方式是指移动文件内部的位置指针到需要读写的位置,在进行读写。按照要求移动位置指针的操作成为文件的定位。
3.1 文件定位
移动文件内部位置指针的函数主要有两个,即rewind函数和fseek函数。
rewind函数前面多次使用,其调用形式为:
rewind(文件指针);
它的功能是把文件内部的位置指针移到文件首。
下面主要介绍fseek函数。fseek函数用来移动文件内部位置指针,其调用形式为:
fseek(文件指针,位移量,起始点);
其中:
(1)“文件指针”指向被移动的文件。
(2)“位移量”表示移动的字节数,要求位移量是long型数据,以便在文件长度大于64KB时不会出错。当用常量表示位移量时,要求加后缀“L”。
(3)“起始点”表示从何处开始计算位移量,规定的起始点有3中:文件首,当前位置和文件尾。
其表示方法如下表所示:
起始点 | 表示符号 | 数字表示 |
文件首 | SEEK_SET | 0 |
当前位置 | SEEK_CUR | 1 |
文件末尾 | SEEK_END | 2 |
如:
fseek(fp,100L,0);
其意义是把位置指针移到距离文件首100个字节处。
还要说明的是fseek函数一般用于二进制文件。在文本文件中由于要进行转换,故往往计算的位置会出现错误。
3.2 文件的随机读写具体操作方法
在移动位置指针之后,即可使用前面介绍的任一种读写函数进行读写。由于一般是读写一个数据块,因此常用fread和fwrite函数。
例10:使用随机读取文件
#include<stdio.h> #include<stdlib.h> #define NUM 20 main( ) { FILE *fp1; /*定义文件指针*/ char *temp; int i,j; struct rec{ /*定义结构体类型*/ char id[10]; char name[15]; char department[15]; }record[NUM]; printf("***********************************************************/n"); printf("** This program is to show the random file input & output**/n"); printf("***********************************************************/n"); if ((fp1=fopen("d://infile.txt","wb"))==NULL) /*以二进制只写方式打开文件*/ { printf("cannot open file");/*出错返回*/ exit(1); } for( i=0;i<NUM;i++) { printf("Please input id:"); scanf("%s",record[i].id); /*从键盘输入*/ printf("Please input name:"); scanf("%s",record[i].name); printf("Please input department:"); scanf("%s",record[i].department); fwrite(&record[i],sizeof(struct rec),1,fp1); /* 成块写入*/ } fclose(fp1); /*关闭*/ if((fp1=fopen("d://infile.txt","rb+"))==NULL)/*以可读写方式打开文件*/ { printf("cannot open file"); /*出错返回*/ exit(1); } printf("************************************/n"); printf("%-10s%-15s%-15s/n","id","name","department"); printf("************************************/n"); for (i=0;i<NUM;i++) { /*显示全部文件内容*/ fread(&record[i],sizeof(struct rec),1,fp1); printf("%-10s%-15s%-15s/n",record[i].id,record[i].name,record[i].department); } /*以下进行文件的随机读写*/ fseek(fp1,2*sizeof(struct rec),0); /* 定位文件指针指向第三条记录*/ fwrite(&record[1],sizeof(struct rec),1,fp1); /* 在第三条记录处写入第二条记录*/ rewind(fp1); /*移动文件指针到文件头*/ printf("************************************/n"); printf("%-10s%-15s%-15s/n","id","name","department"); printf("************************************/n"); for (i=0;i<NUM;i++) { /*重新输出文件内容*/ fread(&record[i],sizeof(struct rec),1,fp1); printf("%-10s%-15s%-15s/n",record[i].id,record[i].name,record[i].department); } fclose(fp1); /*关闭文件*/ scanf("%d",i); }
3.3 文件检测函数
C语言常用的文件检测函数有以下几个:
1.文件结束检测函数feof
feof(文件指针);
功能:判断文件是否处于文件结束为止,如文件结束,则返回值1,否则返回值0.
2.读写文件出错检测函数ferror
ferror函数的调用格式为:
ferror(文件指针);
功能:检查文件在用各种输入输出函数进行读写时是否出错。如ferror的返回值为0,表示未出错,否则表示有错。
3.文件出错标志和文件结束置0函数
clearerr函数的调用格式:
clearerr(文件指针);
功能:本函数用于清除出错标志和文件结束标志,使他们为0值。