fwrite
, fread
, write
, 和 read
都是用于文件输入输出的函数,但它们之间存在一些关键区别:
-
缓冲与非缓冲:
fread
和fwrite
是带缓冲区的函数,这意味着它们在用户空间和文件之间操作时会利用缓冲区来提高效率。标准I/O库会自动管理这个缓冲区,减少了直接磁盘访问的次数。read
和write
是系统级的、无缓冲的函数,直接与文件描述符操作,不经过标准I/O库的缓冲区。这使得它们的执行速度可能更快,尤其是在大数据块传输时,但这也意味着程序员需要自己处理缓冲逻辑以优化性能。
-
函数来源与接口:
fread
和fwrite
属于C标准库函数,使用文件指针(通过fopen
获得)进行操作,提供了较为高级且易于使用的接口。read
和write
是Unix/Linux系统调用,使用文件描述符(通常通过open
获得)进行操作,属于低级接口,提供了更底层的功能。
-
数据处理:
fread
和fwrite
更适合处理结构化数据,因为它们可以按数据项的大小和数量来读写,适合读写固定长度的数据结构,如结构体。read
和write
操作的是字节流,对于处理任意类型的数据(包括文本和二进制)都非常灵活,但在处理复杂数据结构时可能需要手动解析。
-
错误处理和返回值:
fread
和fwrite
返回实际读取或写入的元素数量,如果发生错误或到达文件末尾,返回值可能会小于请求的数量。read
和write
返回实际读取或写入的字节数,负值表示发生了错误(此时可以使用errno
获取错误码)。
-
文件权限与打开模式:
- 使用
fopen
时,无法直接指定文件权限,而open
允许直接指定文件的访问权限。
- 使用
-
适用场景:
- 当需要处理较高层次的文件操作,特别是涉及到结构化数据或者希望利用缓冲提升效率时,推荐使用
fread
和fwrite
。 - 对于底层编程、需要精细控制文件访问或者对性能有严格要求的场景,可能会偏向于使用
read
和write
,尽管这通常需要更多的代码来管理缓冲和错误处理。
- 当需要处理较高层次的文件操作,特别是涉及到结构化数据或者希望利用缓冲提升效率时,推荐使用
综上所述,选择使用哪个函数取决于具体需求,比如是否需要缓冲、数据处理的复杂度以及对性能和控制的需求。
fread函数
fread
函数是C语言标准库中用于从文件流中读取数据的一个重要函数。它的原型定义如下:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
-
参数说明:
ptr
:一个指针,指向接收读取数据的缓冲区。这个缓冲区应该足够大以容纳size * count
个字节的数据。size
:单个元素的大小,以字节为单位。例如,如果你要读取整数,size
应为sizeof(int)
。count
:要读取的元素数量。因此,总共将读取size * count
个字节。stream
:指向已打开的文件流的指针,该文件先前由fopen
打开。
-
- 要读取整数,
size
应为sizeof(int)
。 count
:要读取的元素数量。因此,总共将读取size * count
个字节。stream
:指向已打开的文件流的指针,该文件先前由fopen
打开。
- 要读取整数,
-
功能:
fread
尝试从文件流stream
中读取count
个元素,每个元素大小为size
字节,并将读取的数据存储到由ptr
指向的缓冲区中。如果文件流支持它,这个操作可能是块读取,即一次读取多个元素。
-
返回值:
- 成功时,
fread
返回实际读取的元素数量。如果到达文件末尾或者遇到读取错误,返回值可能小于count
。如果发生错误,可以通过调用ferror(stream)
来检查错误状态,通过feof(stream)
来检测是否到达文件末尾。
- 成功时,
-
注意事项:
fread
不会自动增加文件位置指示器。如果你在同一个文件流上连续调用fread
,除非显式调用如fseek
或rewind
等函数改变位置,否则读取将会从上一次调用结束后的位置继续。- 由于
fread
不区分读取错误和文件末尾,调用者必须通过检查feof
和ferror
来确定读取操作的状态。 - 使用
fread
时,最好确保缓冲区足够大以容纳所有预期的数据,避免内存溢出。
fwrite函数
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
-
参数说明:
ptr
:指向要写入数据的缓冲区的指针。这个缓冲区应该包含size * count
个字节的数据。size
:单个元素的大小,以字节为单位。例如,如果你要写入整数,size
应为sizeof(int)
。count
:要写入的元素数量。因此,总共将尝试写入size * count
个字节。stream
:指向已打开的文件流的指针,该文件由fopen
函数打开,并且通常是以写入模式(如"wb")打开的。
-
功能:
fwrite
尝试将count
个元素,每个元素大小为size
字节,从ptr
指向的缓冲区写入到文件流stream
中。此操作可以是块写入,即一次写入多个元素,这依赖于底层系统的实现和文件的打开模式。
-
返回值:
- 成功时,
fwrite
返回实际写入的元素数量。这个数字可能小于count
,如果发生了错误或写入被中断。如果返回值小于count
,应该通过调用ferror(stream)
来检查是否有错误发生。
- 成功时,
-
注意事项:
- 在调用
fwrite
前,确保文件已以适当的模式(如二进制写入"wb")打开,并且文件指针位于期望的写入起始位置。 fwrite
不会自动刷新缓冲区到磁盘。为了确保数据立即写入磁盘,可以调用fflush(stream)
函数。- 写入操作可能会受到文件系统缓存的影响,因此即使调用了
fwrite
,数据也可能并未立即物理写入磁盘。
- 在调用
open函数
#include <fcntl.h>
#include <unistd.h>int open(const char *pathname, int flags, mode_t mode);
-
参数说明:
pathname
:指向包含要打开或创建的文件路径名的字符串指针。flags
:指定打开文件的操作模式,可以是以下标志的组合(部分常见标志):O_RDONLY
:以只读方式打开。O_WRONLY
:以只写方式打开。O_RDWR
:以读写方式打开。O_CREAT
:如果文件不存在,则创建之。需同时指定mode
。O_TRUNC
:如果文件存在并且以写入模式打开,则将其长度截断为0。O_APPEND
:写入时,数据追加到文件末尾而不是覆盖现有内容。- 其他更多高级标志,如
O_EXCL
用于与O_CREAT
一起避免文件被意外覆盖等。
mode
:当与O_CREAT
一起使用时,指定新创建文件的权限位。通常使用类似S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
这样的组合来设置所有者、同组用户和其他用户的读写权限。
-
返回值:
- 成功时,返回一个非负的文件描述符。
- 失败时,返回-1,并且可以通过
errno
变量获取错误代码。
read函数
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
-
参数说明:
fd
:要从中读取数据的文件描述符。这是一个非负整数,之前通过open
或其他系统调用获得。buf
:指向缓冲区的指针,这个缓冲区用于存放从文件描述符中读取的数据。缓冲区必须足够大以容纳count
字节的数据。count
:要读取的字节数。这是你期望从文件描述符中读取的最大数据量。
-
功能:
read
尝试从文件描述符fd
中读取最多count
个字节的数据,并将其存储到由buf
指向的缓冲区中。实际读取的字节数可能少于请求的字节数,这取决于多种因素,比如文件的实际剩余字节数、文件描述符的状态或是否设置了非阻塞模式。
-
返回值:
- 成功时,
read
返回实际读取的字节数。如果返回0,表示到达文件末尾;如果返回-1,则表示发生错误,此时可以通过检查全局变量errno
来获取具体的错误代码。
- 成功时,
-
阻塞行为:
- 默认情况下,如果请求的数据不可立即读取(例如,到达文件末尾或没有数据可读),
read
函数会在某些类型的文件描述符上阻塞,直到数据可用或发生错误。然而,对于非阻塞模式下的文件描述符,如果当前没有数据可读,read
会立即返回,可能返回0(文件末尾)或-1(并且设置errno
为EAGAIN
或EWOULDBLOCK
)。
- 默认情况下,如果请求的数据不可立即读取(例如,到达文件末尾或没有数据可读),
write函数
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
-
参数说明:
fd
:要写入数据的文件描述符,一个非负整数,之前通过open
、socket
等系统调用获得。buf
:指向包含要写入数据的缓冲区的指针。count
:要写入的字节数,即缓冲区中数据的大小。
-
功能:
write
尝试将count
个字节的数据从buf
指向的缓冲区写入到文件描述符fd
所关联的资源中。实际写入的字节数可能小于请求的字节数,这取决于系统缓冲区的可用性、文件系统的限制等因素。
-
返回值:
- 成功时,
write
返回实际写入的字节数。如果返回值小于count
,可能是因为部分数据被缓冲等待写入或因其他原因未立即完成写入。 - 如果返回-1,则表示发生了错误,此时可以通过检查全局变量
errno
来获取具体的错误代码。
- 成功时,
-
阻塞行为:
- 对于某些类型的文件描述符,如果缓冲区满或资源暂时不可写,
write
默认可能会阻塞,直到有更多的空间或资源变为可写。但是,如果文件描述符被设置为非阻塞模式,write
在无法立即写入全部数据时可能返回-1,并设置errno
为EAGAIN
或EWOULDBLOCK
。
- 对于某些类型的文件描述符,如果缓冲区满或资源暂时不可写,