目录
一、文件I/O与标准I/O的区别(open与fopen)1、来源2、移植性3、适用范围4、文件IO层次5、缓冲二、函数fopen、fwrite、fread、fseek、fclose1、函数fopen2、函数fwrite3、函数fread4、函数fseek5、函数fclose三、用标准I/O库的函数将整数、结构体和结构体数组写进文件1、将整数写进文件2、将结构体写进文件3、将结构体数组写进文件四、函数fputc、fputs、feof、fgetc、fgets1、fputc和fputs2、feof3、fgetc和fgets一、文件I/O与标准I/O的区别(open与fopen)
1、来源
从来源的角度看,两者能很好的区分开,这也是两者最显而易见的区别:
open
是UNIX系统调用函数(包括LINUX等),返回的是文件描述符(File Descriptor),它是文件在文件描述符表里的索引。fopen
是ANSIC标准中的C语言库函数,在不同的系统中应该调用不同的内核api。返回的是一个指向文件结构的指针。
PS:从来源来看,两者是有千丝万缕的联系的,毕竟C语言的库函数还是需要调用系统API实现的。
2、移植性
这一点从上面的来源就可以推断出来,fopen
是C标准函数,因此拥有良好的移植性;而open
是UNIX系统调用,移植性有限。如windows下相似的功能使用API函数CreateFile
。
3、适用范围
open
返回文件描述符,而文件描述符是UNIX系统下的一个重要概念,UNIX下的一切设备都是以文件的形式操作。如网络套接字、硬件设备等。当然包括操作普通正规文(Regular File)。fopen
是用来操纵普通正规文件(Regular File)的。4、文件IO层次
如果从文件IO的角度来看,前者属于低级IO函数,后者属于高级IO函数。低级和高级的简单区分标准是:谁离系统内核更近。低级文件IO运行在内核态,高级文件IO运行在用户态。
5、缓冲
1、缓冲文件系统
缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用;当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”,装满后再从内存“缓冲区”依此读出需要的数据。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存“缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器 而定。fopen, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind
等。
2、非缓冲文件系统
缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、字符串、格式化数据,也可以读写二进制数据。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出,它不设文件结构体指针,只能读写二进制文件,但效率高、速度快,由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它。open, close, read, write, getc, getchar, putc, putchar
等。
两种I/O模型的比较
一句话总结一下,就是open
无缓冲,fopen
有缓冲。前者与read
,write
等配合使用, 后者与fread
,fwrite
等配合使用。
使用fopen
函数,由于在用户态下就有了缓冲,因此进行文件读写操作的时候就减少了用户态和内核态的切换(切换到内核态调用还是需要调用系统调用API:read
,write
);而使用open
函数,在文件读写时则每次都需要进行内核态和用户态的切换;表现为,如果顺序访问文件,fopen
系列的函数要比直接调用open
系列的函数快;如果随机访问文件则相反。
二、函数fopen、fwrite、fread、fseek、fclose
前提知识
<文件指针>
文件指针实际上是指向一个结构体类型的指针,这个结构体中包含有诸如:缓冲区的地址、在缓冲区中当前存取的字符的位置、对文件是“读”还是“写”、是否出错、是否已经遇到文件结束标志等信息。用户不必去了解其中的细节,所有一切都在 stdio.h 头文件中进行了定义。一般称上面提到的结构体类型名为 FILE,定义文件类型指针变量的一般形式为:
FILE *指针变量名;
例如:
FILE *fp1, *fp2;
fp1
和fp2
均被定义为指向文件类型的指针变量,称为文件指针。
1、函数fopen
函数原型:
#include <stdio.h>FILE *fopen(const char *pathname, const char *mode);
参数介绍:
pathname:包含要打开的文件路径及文件名(含路径,缺省为当前路径);
mode:文件打开状态;
mode 打开模式:
返回值:
若成功,返回指向FILE的指针(文件指针)
若出错,返回NULL
2、函数fwrite
函数原型:
#include <stdio.h>size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
参数介绍:char *str = "Apibro is very nice!";
ptr:存放写入记录的缓冲区(相当于buf缓冲区)
size:写入的记录大小(要写多少字节数),即
sizeof(char)
nmemb:写入的记录数(要写多少个数据项,每个数据项长度为size),即
strlen(str)
stream:要写入的文件流(哪个文件)
返回值:
若成功,返回实际写入的nmemb数目(size_t nmemb为一个整型数,即写入的次数)
若出错,EOF(End of File)
3、函数fread
函数原型:
#include <stdio.h>size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
参数介绍:char *str = "Apibro is very nice!";
ptr:存放读入记录的缓冲区(相当于buf缓冲区)
size:读取的记录大小(要读多少字节数),即
sizeof(char)
nmemb:读取的记录数(要读多少个数据项,每个数据项长度为size),即
strlen(str)
stream:要读取的文件流(哪个文件)
返回值:
若成功,返回实际读取的nmemb数目(size_t nmemb为一个整型数,即读取的次数)
若出错,EOF(End of File),即-1
4、函数fseek
函数原型:
#include <stdio.h>int fseek(FILE *stream, long offset, int whence);
参数介绍:
stream:要操作的文件流(哪个文件)
offset:偏移量
whence:当前位置的基点
与lseek类似,见【Linux】系统编程之文件(常用API)----函数lseek
返回值:
若成功 , 返回0
若失败,返回非0, 并设置error错误代码
5、函数fclose
函数原型:
#include <stdio.h>int fclose(FILE *stream);
参数介绍:
stream:已打开的文件指针
返回值:
若成功 , 返回0
若出错,EOF(End of File),即-1
综合示例
代码:
#include <stdio.h>#include <string.h>#include <stdlib.h>int main(){FILE *fp;char *str = "Apibro is very nice!";char *readBuf = NULL;//FILE *fopen(const char *pathname, const char *mode);fp = fopen("./lu.txt","w+");//int n_fwrite = fwrite(str,sizeof(char),strlen(str),fp);第一种//int n_fwrite = fwrite(str,sizeof(char)*strlen(str),1,fp);第二种,这两种写法一个意思int n_fwrite = fwrite(str,sizeof(char)*strlen(str),10,fp);//fwrite返回值与第三个参数有关,第三个参数是多少就多少//int fseek(FILE *stream, long offset, int whence);fseek(fp,0,SEEK_SET);readBuf = (char *)malloc(sizeof(char)*n_fwrite + 10240);//fread(readBuf,sizeof(char),strlen(str),fp);第一种//int n_fread = fread(readBuf,sizeof(char)*strlen(str),1,fp);第二种,这两种写法一个意思int n_fread = fread(readBuf,sizeof(char)*strlen(str),5,fp);//fread返回值也与第三个参数有关,当第三个参数大于等于n_fwrite时,返回值为n_fwrite;当第三个参数小于n_fwrite时,返回值为第三个参数值printf("read data: %s\n",readBuf);printf("write = %d,read = %d\n",n_fwrite,n_fread);fclose(fp);return 0;}
结果:
注意点:
fwrite
返回值与第三个参数有关,第三个参数是多少就多少fread
返回值也与第三个参数有关,当第三个参数大于等于n_fwrite时,返回值为n_fwrite;当第三个参数小于n_fwrite时,返回值为第三个参数值
三、用标准I/O库的函数将整数、结构体和结构体数组写进文件
1、将整数写进文件
代码:
#include <stdio.h>int main(){FILE *fp;//指向文件的一个指针fpint date1 = 66;int date2;fp = fopen("./file1","w+");int n_fwrite = fwrite(&date1,sizeof(int),1,fp);fseek(fp,0,SEEK_SET);int n_fread = fread(&date2,sizeof(int),1,fp);printf("read %d\n",date2);fclose(fp);return 0;}
结果:
2、将结构体写进文件
代码:
#include <stdio.h>struct Test{int a;char c;};int main(){FILE *fp;//指向文件的一个指针fpstruct Test date1 = {100,'c'};struct Test date2;fp = fopen("./file1","w+");int n_fwrite = fwrite(&date1,sizeof(struct Test),1,fp);fseek(fp,0,SEEK_SET);//光标定位int n_fread = fread(&date2,sizeof(struct Test),1,fp);printf("read %d,%c\n",date2.a,date2.c);fclose(fp);return 0;}
结果:
3、将结构体数组写进文件
代码:
#include <stdio.h>struct Test{int a;char c;};int main(){FILE *fp;//指向文件的一个指针fpstruct Test date1[2] = {{100,'c'},{200,'d'}};struct Test date2[2];fp = fopen("./file1","w+");int n_fwrite = fwrite(&date1,sizeof(struct Test)*2,1,fp);fseek(fp,0,SEEK_SET);//光标定位int n_fread = fread(&date2,sizeof(struct Test)*2,1,fp);printf("read %d,%c\n",date2[0].a,date2[0].c);printf("read %d,%c\n",date2[1].a,date2[1].c);fclose(fp);return 0;}
结果:
四、函数fputc、fputs、feof、fgetc、fgets
1、fputc和fputs
作用:
fputc(ch,fp)
把字符ch写到文件指针变量fp所指向的文件中
fputs(str,fp)
把str所指向的字符串写到文件指针变量fp所指向的文件中
函数原型:
#include <stdio.h>int fputc(int c, FILE *stream);int fputs(const char *s, FILE *stream);
参数介绍:
c:要输出的字符
s:要输出的字符串
stream:对应的(输出的)文件流fp
返回值:
若成功,
fputc
返回字符c、fputs
返回s(字符串首地址)非负值若已达到文件尾端或出错,
fputc
返回EOF,即-1、fputs
返回NULL
示例1
代码:
#include <stdio.h>#include <string.h>int main(){FILE *fp;char *str = "Apibro is very nice!";int i;int len = strlen(str);fp = fopen("./test.txt","w+");for(i=0;i<len;i++){fputc(*str,fp);str++;}fputs("qwertyui",fp);fclose(fp);return 0;}
结果:(未用fseek重新定位光标,所以内容接在后面写了)
2、feof
作用:检查到文件读写位置标记是否移到文件的末尾,即,磁盘文件是否结束。
函数原型:
#include <stdio.h>int feof(FILE *stream);
参数介绍:
stream:对应的文件流fp
返回值:
若fp指向的文件结束,函数值为1(真)
若fp指向的文件没有结束,含函数值为0(假)
示例2
用到fgetc,下面有介绍,请往下翻阅。
代码:
#include <stdio.h>#include <string.h>#include <stdlib.h>int main(){FILE *fp;char c;char msg[50] = "Apibro is very nice!";fp = fopen("./test.txt","r+");while(!feof(fp)){//int fgetc(FILE *stream);c = fgetc(fp);//若读到文件尾或出现错误时,它就返回EOFprintf("%c",c); }printf("\n");fclose(fp);return 0;}
结果:
这里读到文件尾或出现错误时,它就返回EOF,也打印出来就多了个问号?
注意:这里
!feof(fp)
,即当fp没有移到文件尾就返回0,!0
就是1,while继续执行;当fp移到文件尾就返回1,!1
就是0,跳出while循环
3、fgetc和fgets
作用:
fgetc(fp)
从fp指向的文件读入一个字符
fgets(str,n,fp)
从fp指向的文件读入一个长度为(n-1)的字符串,存放到字符数组str中
函数原型:
#include <stdio.h>int fgetc(FILE *stream);char *fgets(char *s, int size, FILE *stream);
参数介绍:
s:要输入的字符串
size:输入字符串长度
stream:对应的(输入的)文件流fp
返回值:
若成功,
fgetc
返回下一个字符(带回所读的字符)、fgets
返回s(字符串首地址)若已达到文件尾端或出错,
fgetc
返回EOF,即-1、fgets
返回NULL
示例3
代码:
#include <stdio.h>#include <string.h>#include <stdlib.h>int main(){FILE *fp;char c;char msg[50] = "Apibro is very nice!";char msg2[50];fp = fopen("./test2.txt","r+");//int fputs(const char *s, FILE *stream);fputs(msg,fp);//把msg写到文件fp里//int fseek(FILE *stream, long offset, int whence);fseek(fp,0,SEEK_SET);//光标移至文件头//char *fgets(char *s, int size, FILE *stream);fgets(msg2,50,fp);//读取fp指向的文件中50个字节放到msg2中printf("str=%s\n",msg2);fclose(fp);return 0;}
结果:
最后谢谢阅读,笔者乃小白,如有错误之处还请指正。