分类: C/C++
2016-11-29 11:31:18
原文地址:常用文件操作-标准库函数 作者:zhenhuaqin
标准C定义了文件库函数的原型在
除了3个标准文件外的所有文件在读写前都必须显式的打开。文件打开函数是fopen函数。
fopen函数的原型为:
FILE *fopen(const char *filename, const char * mode);
其中mode是控制该文件的打开方式的参数;filename表示要打开的文件在操作系统下的名称,这个名称应该包括路径名称,具体的名称、路径的约定与操作系统有关。值得注意的是,在MS-DOS、WINDOWS或UNIX系统环境下,关于文件的名称、路径的拼写规则有一些差别。filename 可以是一个表示文件路径和名称的字符串常量,也可以是一个指向字符串的指针变量,被指向的字符串包含要使用的文件的件路径和名称。
mode控制文件被打开后,程序是对文件进行读、写还是其它方式,具体mode的可用值如表9-1所示。
表9-1
Mode |
意 义 |
|
r |
只读方式打开。如果文件不存在,函数返回NULL |
|
w |
写方式打开。如果文件不存在,则创建它;如果文件存在,则删除它,再创建一个新的空文件! |
|
a |
按增加方式打开。如果文件不存在,则创建一个空文件;如果文件存在,则新的数据写到文件最后。 |
|
b |
按二进制文件打开,不加此选项的为文本文件 |
|
+ |
修改(读或写)模式,需要和r、w、a一起使用 |
|
常见的模式包括r、w、a、rb、wb、ab、r+、w+、a+、r+b或rb+、w+b或wb+、a+b或ab+。其中要注意修改模式符“+”是不单独使用的,它需要和r、w、a等模式符一起使用。“b”必须添加模式字符串中,表示要操作的文件是二进制文件。
例如,“r+”表示打开一个文本文件(该文件应该已经存在),可以对文件进行读或写出操作,写入文件的新数据添加在文件的开头,覆盖原有的内容。“w+”表示创建一个新文件,可对该文件进行读写操作。“a+”模式表示打开一个文件,可对该文件进行读或者附加操作,如果该文件已经存在,则写入的数据附加在该文件的末尾,如果该文件不存在,则创建新文件。
需要注意的是,这些打开文件的模式符在不同的操作系统上有一些细微的差别,请大家在开发程序时留意其区别。具体mode形式,可参阅C语言手册。
fopen返回的是一个指向FILE的指针,这个文件指针通常被称为文件句柄,以后对该文件的所有操作全部利用该指针进行,不再使用文件名。如果打开文件失败,则fopen()返回NULL。造成文件不能成功打开的原因有多种,如打开的文件不存在,文件所在的磁盘为准备好或者磁盘未格式化等。
FILE是系统定义的描述文件信息的结构类型标识符,用来记录文件数据流的控制信息,包括文件读写指针、缓冲区、出错标志、文件结束标志等。一般在stdio.h文件中定义。
例如,按只读方式打开一个文本文件,文件名从键盘输入,程序代码如下:
FILE *fp;
char filename[20];
printf(“please input filename:”);
scanf(“%s”,filename);
if ((fp=fopen(filename,”r”))==NULL)
{ printf(“Error opening the file\n”);
exit(1);
}
……
其中exit()作用是中断程序的执行。
使用完文件后应该关闭它,切断文件指针和文件的联系,防止出现对文件的误用。关闭文件的库函数是fclose函数。fclose函数的原型为:
int fclose(FILE *fp);
fclose函数的作用是关闭已经打开的文件,要求操作系统将文件句柄fp所代表的文件系统进行关闭。操作系统完成如下任务:
l 收回程序对该文件的使用权限;
l 将存储在文件缓冲区中的数据,真正写到磁盘文件中。在关闭之前,可能有部分数据存储的文件缓冲区中,当出现意外情况(如断电等),既有可能使得文件中的信息出现错误;
l 修改文件的基本信息,如结束标志等,对于网络、共享系统释放文件的读写锁,允许其它程序对文件进行读写。
例如:
fclose(fp);
C语言中文本文件存储信息的方法是以文本的形式完成的,因此把程序中的数据存储在文本文件中去,需要将其格式转换为文本的形式。例如28036(十六进制形式为0x6D84)存储在文件中,在二进制文件中表示为2个字节的数分别是0x0B、0x14;而在文本文件中存储的形式为5个字节: 0x32(字符’2’)、0x38(字符’8’)、0x30(字符’0’)、0x33(字符’6’)、0x36(字符’6’)。
显然,这个转换过程比较复杂,因此标准C提供关于文本文件读写的标准函数,fprintf()、fscanf()、fgetc()、fputs()等,下面讨论主要的文本输入输出函数。
一、fprintf函数
fprintf函数原型如下:
int fpintf(FILE *fp, char *mode, …);
fprintf()函数与printf()函数比较,格式中多了一个fp参数,其作用和意义基本相同,唯一不同的作用是前者将输出的文本信息不是写入到标准输出文件中,而是写入到由fp指向的文件中。
函数的意义是:将省略号表示的位置列出的表达式的值计算出来后,按mode中指定的格式输到有fp指向的文件中。
例9.1 有10个学生考了C语言这门课,编程将学生成绩输入到文件c:\data\score.txt中。
#include
int main(void)
{ FILE *fp;
int i,grade;
if((fp=fopen(“c:\\data\\score.txt”,”w”))==NULL)
{ fprintf(stderr,“Error opening file c:\\data\\score.txt\n”);
exit(1);
}
printf(“input 10 score:\n”);
for(i=1;i<=10;i++)
{ scanf(“%d”,&grade);
fprintf(fp,”%5d”,grade);
if(i==5) fprintf(fp,“\n”);
}
fclose(fp);
return 0;
}
程序运行结果如下:
input 10 score:
86 60 55 98 76 62 43 87 70 83↙(用户输入)
程序中创建新的文本文件c:\data\score.txt,用户可使用任意一个编辑软件打开该文件看到该文件内容如下:
□□□86□□□60□□□55□□□98□□□76
□□□62□□□43□□□87□□□79□□□83
其中,“□”表示空格符。
二、fscanf函数
fscanf函数原型如下:
int fscanf(FILE *fp, const char *mode, …);
fscanf ()函数与scanf()函数比较,格式中多了一个fp参数,其作用和意义基本相同,唯一不同的作用是后者将是从标准输入文件中,按指定格式逐个输入信息到指定的变量中;后者是从fp指向的文本文件中读取数据。
函数的意义是:从fp指向的文件中,按mode中指定的格式逐个读取文本数据转换成指定的数据类型,附给对应指针指向的存储单元中。
例9.2 将例9.3-1中创建的文件c:\data\score.txt中数据读出来并显示。
#include
int main(void)
{ FILE *fp;
int i=0,grade;
if((fp=fopen(“c:\\data\\score.txt”,”r”))==NULL)
{ fprintf(stderr,“Error opening file c:\\data\\score.txt\n”);
exit(1);
}
printf(“scores :\n”);
while(!feof(fp)) /*如果fp指向文件结束位置,则feof(fp)返回值为真*/
{ fscanf(fp,“%d”,&grade);
printf(”%5d”,grade);
i++;
if(i==5) printf(“\n”);
}
printf(“\nThere are %d number\n”,i);
fclose(fp);
return 0;
}
程序运行结果如下:
86 60 55 98 76
62 43 87 79 83
程序中用到库函数feof(),该函数用于判断文件指针是否指向文件结束位置,如果指向文件结束位置,则该函数返回非0数据;否则返回0。
三、字符输入函数
字符输入函数fgetc()原型如下:
int fgetc(FILE *fp);
fgetc()函数是从句柄fp指向的文件当前位置读一个字符作为函数的返回值。
另外,还有函数char *fgets(char *str, int n, FILE *fp)是从文件中读取最多n个字符到str指向存储单元。还有其它一些宏或函数也可以进行字符输入,请大家参阅附录B。
例如:
c=fgetc(fp);
从fp指向的文件当前位置读一个字符,并赋给变量c,并使文件指针下移一个字符。如果fp指向文件结束位置,则返回文件结束标志EOF(即为-1)。
四、字符输出函数
字符输出函数fputc的原型如下:
int fputc(int ch , FILE *fp);
fputc()函数是将字符ch直接写入句柄fp指向的文件的当前位置,如果出错则返回EOF。另外,还有函数int fputs(char *str, int n, FILE *fp),其作用类似与puts()函数。
例9.3 输入源文件名和目标文件名,编程实现文件复制。
#include
int main(void)
{ FILE *fp1,*fp2;
char c;
char source[20],destine[20];
printf(“please input source filename:);
scanf(“%s”,source);
if((fp1=fopen(source,”r”))==NULL)
{ fprintf(stderr,“Error opening file %s\n”,source);
exit(1);
}
printf(“please input destine filename:);
scanf(“%s”, destine);
if((fp2=fopen(destine,”w”))==NULL)
{ fprintf(stderr,“Error opening file %s\n”,destine);
exit(1);
}
while(!feof(fp1))
{ c=fgets(fp1);
fputs(c,fp2);
}
printf(“\nOk!\n”,i);
fclose(fp1);
fclose(fp2);
return 0;
}
对二进制文件读写的库函数是fread()和fwrite(),由于它们用来将二进制代码的数据惊醒输入和输出,因此又称为直接输入输出函数。
1. read函数
读二进制文件的函数fread()原型:
int fread(void *buf, int size, int count, FILE *fp);
fread()函数从fp指向的二进制文件中,读入count个大小为size个字节的数据块到buf所指向的内存中。如果执行成功,则返回实际读取的数据块的个数。
例如,从已经打开的文件指针f所指向的文件中读入10个长整数到数组long a[20]中,这10个数依次存储到a[9]开始的10个元素位置。
if (fread(&(a[9]), 4, 10, f) != 10)
printf(”从文件读出现错误!\n”);
当然要注意该语句执行的数据是否正确,决定与f指向的文件中的确存储的是否long类型的数据,只有文件中存储的是long类型的数据,上述语句才有意义。
例如,从已经打开的文件指针f所指向的文件中读入28个结构数据到数组cs2003(类型标识符设为student)中。
if (fread(cs2003, sizeof(student), 28, f) != 28)
printf(”从文件中读学生数据出现错误!\n”);
2. write函数
写二进制文件的库函数fwrite()原型:
int fwrite(const void *buf, int size, int count, FILE *fp);
fwrite()函数从buf所指向的内存中,读入count个大小为size个字节的数据块写入到fp指向的文件中。如果执行成功,则返回实际写入的数据块的个数。
例如,将长整数数组a[20]的前10个元素写入文件f中。
if (fwrite(a, sizeof(long), 10, f) != 10)
printf(”文件写出现错误!\n”);
例如,将结构数据到数组cs2003的28个数据(类型标识符设为student)写入文件ff中。
if (fwrite(cs2003, sizeof(student), 28, f) != 28)
printf(”从文件中读学生数据出现错误!\n”);
例9.4 输入10个学生数据到文件c:\student.dat中,然后从文件中把数据读出来并显示。
#include
struct stud{
char name[20];
int age;
long num;
}s[10],t;
int main(void)
{ FILE *fp;
int i=0;
if((fp=fopen(“d:\\student.dat”,”wb”)
{ printf(“Error opening file d:\\student.dat\n”);
exit(1);
}
while(i<10)
{ printf(“input name:”); scanf(“%s”,s[i].name);
printf(“input age:”); scanf(“%d’,&s[i].age);
printf(“input number:”); scanf(“%ld”,&s[i].num);
}
if(fwrite(s,sizeof(struct stud),10,fp)!=10)
{ printf(“Error writing file d:\\student.dat\n”);
exit(1);
}
fclose(fp);
if((fp=fopen(“d:\\student.dat”,”rb”)
{ printf(“Error opening file d:\\student.dat\n”);
exit(1);
}
i=0;
while(!feof(fp))
{ if(fread(&t,sizeof(struct stud),1,fp)!=1)
{ fprintf(“Error reading file d:\\student.dat\n”);
exit(1);
}
i++;
printf(“the %dth student:”,d);
printf(“ name:%s”,t.name);
printf(“age:%d’,t.age);
printf(“number:%ld\n”,t.num);
}
fclose(fp);
return 0;
}
文件的输入输出一般情况下是按顺序访问的,文件指针在文件刚打开是指向文件的开始位置,每次进行文件读写后,自动移动到下一个位置,使得文件读写严格地一个一个地进行下去。但是,在程序设计过程中,有时需要以任意顺序访问一个文件,C语言提供系统调用函数fseek来改变文件指针的位置,其函数原型是:
int fseek(FILE *fp, long int offset, int origin);
将已经打开的文件fp的当前读写位置(一般称为文件指针)移动到由offset和origin所确定的位置。其中offset为偏移的字节数,以origin指定的文件位置为基准,根据offset为正数或负数决定向后或前移。如果文件指针成功设置,则函数返回值为0,否则为非0值。其中origin的可能取值为:
l SEEK_SET:文件的开始位置;
l SEEK_CUR:文件的当前位置;
l SEEK_END:文件的结束位置。
其中,SEEK_SET、SEEK_CUR和SEEK_END依次为0,1和2。
另外一个常用的改变文件指针位置的库函数是rewind(),该函数原型是:
void rewind(FILE *fp);
rewind()函数的功能是将文件指针fp重新指向到文件开始位置。
例9.5 输入一个文件名,读出该文件内容并反向显示出来。
#include
int main(void)
{ FILE *fp;
char filename[20];
printf(“please input filename:”);
scanf(“%s”,filename);
if((fp=fopen(filename,”rb”))==NULL)
{ printf(“Error opening file %s\n”,filename(;
exit(1);
}
fseek(fp,1L,2); /*设置文件指针位于文件结束标记的前一个位置*/
while(fseek(fp,-2L,1)) /*文件指针退回一个位置*/
putchar(fgetc(fp));
return 0;
}
移动、判定文件位置的方法有许多,请大家在实践过程中参照有关的C语言手册和资料。
1 feof()函数
注意在读文件的时候,我们往往需要判定是否到达文件的结束位置,避免文件读写错误。feof()函数原型为:
int feof(FILE *stream)
该函数测试stream所指的文件是否已经结束,如果文件指针已经指向文件结束位置,该函数返回非0值,说明stream指向的文件当前位置已经到达文件的结束位置。
2 ferror()函数用于确定文件操作是否出错,其原型为:
int ferror(FILE *fp)
函数返回0表示此前的文件操作成功,否则,返回非0值表示此前的文件操作出错。
3 clearerr()函数用于清除文件结束标志和文件出错标志,其原型为:
void clearerr(FILE *fp)