fopen、fread、fwrite及open、read、write区别
一、fopen和open的区别
(1)来源
open是UNIX系统调用函数(包含LINUX等),返回的是文件描述符(File Description),它是文件在文件描述符表里的索引。
fopen是ANSIC标准中的C语言库函数,在不同的系统中应调用不同的内核API,返回一个指向文件结构的指针。
(2)移植性
这一点从上面的来源就可以推断出来,‘fopen’是C标准函数,因此拥有良好的移植性;而‘open’是UNIX系统调用的,移植性有限。如:windows下相似的功能使用API函数‘CreateFile’。
(3)适用范围
open返回文件描述符,而文件描述符是UNIX系统下的一个重要概念,UNIX下的一切设备都是以文件的形式操作。如网络套接字、硬件设备等,也包括操作普通正规文件(Regular File)
fopen是用来操作普通正规文件(Regular File)
(4)缓冲/非缓冲文件系统
1、缓冲文件系统
缓冲文件系统是借助于文件结构体指针 FILE* 来对文件进行管理,通过文件指针对文件进行访问,即可以读写字符、字符串、格式化数据,也可以读写二进制数据
缓冲文件系统特点:
在内存中开辟一个“缓冲区”,为程序里每一个文件使用,当执行读文件操作时,从磁盘文件将数据先读入内存“缓冲区”,装满后再从内存“缓冲区”依次读入接收的变量。执行写文件操作时,也是先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存“缓冲区”的大小,影响着实际操作外在的次数,内存“缓冲区”越大,则操作外存的次数就越少,执行速度就越快,效率就越高。一般来说,文件“缓冲区”的大小跟机器是相关的。
缓冲文件系统的IO函数主要包括:
fopen, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind等
2、非缓冲文件系统
- 非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出
- 它不设文件结构体指针,只能读写二进制文件(对于UNIX系统内核而言,文本文件和二进制代码文件并无区别),但效率高、速度快,由于ANSI标准不再包括非缓冲文件系统,因此,在读取正规的文件时,建议大家最好不要选择它。
非缓冲文件系统的IO函数主要包括:
open, close, read, write, getc, getchar, putc, putchar等
(5)文件IO层次
open:低级IO函数
fopen:高级IO函数
高低级根据谁离系统内核更近
低级:运行在内核态
高级:运行在用户态
(6)补充
对缓冲和非缓冲文件系统的补充,举个例子来更清晰的了解什么情况下用缓冲,什么情况下用非缓冲的
如果文件的大小是8k。
你如果用read/write,且只分配了2K的缓存,则要将此文件读出需要做4次系统调用来实际从磁盘上读出。如果你用fread/fwrite,则系统自动分配缓存,则读出此文件只要一次系统调用从磁盘上读出。也就是用read/write要读4次磁盘,而用fread/fwrite则只要读1次磁盘。效率比read/write要高4倍。
如果程序对内存有限制,则用read/write比较好。都用fread 和fwrite,它自动分配缓存,速度会很快,比自己来做要简单。
如果要处理一些特殊的文件,用read 和write,如 套接口,管道之类的设备文件。
系统调用write的效率取决于你buffer的大小和你要写入的总数量,如果buffer太小,你进入内核空间的次数大增,效率就低下。而fwrite会替你做缓存,减少了实际出现的系统调用,所以效率比较高。
二、fread、fwrite和read、write的区别
(1)fread、fwrite是带缓冲的,read、write不带缓冲
fopen、fread、fwrite的简单应用
一、fopen函数
头文件:
#include <stdio.h>
函数:
FILE *fopen(const char *path, const char *mode);
第一个参数:文件路径
第二个参数:以什么权限访问文件
权限 二进制文件
r:只读打开文本文件 rb
r+:可读可写打开文本文件 rb+
w:只写打开文本文件 wb
w+:可读可写方式创建一个文本文件 wb+
a:追加方式打开文本文件 ab
a+:可读可写可追加的方式打开一个文本文件 ab+
二、fread函数
头文件
#include <stdio.h>
函数
size_t fread(void *ptr, size_t size, size_t nmenb, FILE *stream)
第一个参数:是读取的数据存放的内存的指针, (可以是数组,也可以是新开辟的空间)
第二个参数:是每次读取的字节数
第三个参数:是读取的次数
第四个参数:是要读取的文件的指针
返回值
成功:是实际读取的元素(并非字节)数目,当目标缓冲区是20字节时,而读100次,返回的是实际读到有字节的次数。
失败:返回0
三、fwrite函数
头文件
#include <stdio.h>
函数
size_t fwrite(const void *ptc, size_t size, size_t nmenb, FILE *stream);
第一个参数:是读取的数据存放的内存的指针, (可以是数组,也可以是新开辟的空间)
第二个参数:是每次读取的字节数
第三个参数:是读取的次数
第四个参数:是要读取的文件的指针
返回值:
成功,该函数返回一个 size_t 对象,表示元素的总数(其实就是nmemb),该对象是一个整型数据类型。如果该数字与 nmemb 参数不同,则会显示一个错误。
失败:返回0
四、fseek函数
头文件
#include <stdio.h>
函数
int fseek( FILE *stream, long offset, int origin );
第一个参数:stream为文件指针
第二个参数:offset为偏移量,整数表示正向偏移,负数表示负向偏移
第三个参数:origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
其中SEEK_SET,SEEK_CUR和SEEK_END和依次为0,1和2
返回值:成功返回0
失败返回非0
五、其他
fputc函数:
int fputc(int c, FILE *stream);
参数:
c:字符
fp:文件指针
返回值:
成功:返回写入的字符
失败:返回EOFfputs函数:
int fputs(const char *s, FILE *stream);
参数:
s:字符串
fp:文件指针
返回值:
成功:返回非负数
失败:返回EOFfgetc函数:
int fgetc(FILE *stream);
从文件fp中读入一个字节,指针自动后移
为什么fgetc返回的是int而不是char?
通过man fgetc查看其返回值:fgetc(), getc() and getchar() return the character read as an unsigned char cast to an int or EOF on end of file or error.
返回时,unchar型会被转换为int,因为读到文件尾时需要返回的是EOF(-1), 如果为char型,-1与0XFF相等,会发生冲突
返回值:成功,则返回读取的“字节”,同于fputc函数的 ch,到达文件尾,返回EOF结束
fgets函数
char * fgets(char *str, int n, FILE *fp)
从文件中每次读入一行。
str是存放将要读入字符型数据的存放地址
n是数目变量
fp是文件指针
该函数表明从文件fp中读入n-1个字符放在以str为起始地址的空间
因为系统会在最后加上一个‘\0’。读入过程中:
若该行字符数不超过n-2个(2代表该行本身的换行符‘\n’和系统自动加上的'\0')
则函数读完该行就结束,文件指针移动到下一行,再进行文件读入操作时,从下一行开始读入。若该行字符数目总数(不包含换行符)大于n-2,则只读入n-1个字符(1代表系统自己加上的'\0')
文件指针自动移动到下一个字符处,再进行文件读入操作时,顺序读入该行剩下的字符。返回值:
文件读入成功则返回str失败或者读到文件尾则返回null
feof 函数
int feof(FILE *stream);
feof()是检测流上的文件结束符的函数,如果文件结束,则返回非0值,否则返回0