C语言中read与fread如何选择使用?

99ANYc3cd6
预计阅读时长 16 分钟
位置: 首页 C语言 正文

核心区别:C标准库 vs. 操作系统API

这是理解两者最关键的一点:

c语言 read fread
(图片来源网络,侵删)
  • fread (Formatted Read)

    • 所属库: C标准库,也就是我们常说的 stdio.h
    • 性质: 是一个高级I/O函数,它被设计为缓冲的,并且是可移植的,可以在任何支持C标准的操作系统(如Windows, Linux, macOS)上以相同的方式工作。
    • 工作方式: fread 并不直接与硬件(如磁盘)交互,它会通过一个缓冲区来操作,当你调用 fread 时,它可能先从文件描述符(底层操作系统资源)所关联的缓冲区读取数据,如果缓冲区为空,它会自动调用更底层的系统函数(在Linux/Unix上是 read,在Windows上是 ReadFile)来填充缓冲区,这个过程称为缓存I/O优化
  • read (Read)

    • 所属库: POSIX标准,通常通过 unistd.h (在Linux/Unix/macOS上) 或 io.h (在Windows上) 引入。
    • 性质: 是一个底层I/O系统调用,它是操作系统提供给应用程序的接口,直接与内核交互。
    • 工作方式: read无缓冲的(或者说,缓冲区由操作系统内核管理,应用程序无法直接控制),它直接从文件描述符(一个整数,代表一个打开的文件、套接字等)读取原始字节到指定的内存缓冲区,它不关心数据的格式,只负责搬运字节。

函数原型详解

fread

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • 参数:
    • void *ptr: 指向一个内存块的指针,fread 会将读取到的数据存放到这里。
    • size: 每个数据项的大小(单位:字节),如果你想读取一个 int 数组,size sizeof(int)
    • nmemb: 你想要读取的数据项的个数。
    • FILE *stream: 一个指向 FILE 对象的指针,这个 FILE 对象代表了你想要读取的流(通过 fopen 打开的文件)。
  • 返回值:
    • 成功时,返回实际读取到的数据项的个数(注意,不是字节数!)。
    • 如果到达文件末尾或发生错误,返回值可能小于 nmemb
    • 如果发生错误或读到文件末尾且没有读取任何数据,则返回 0,你可以使用 feof()ferror() 函数来区分是文件结束还是错误。
  • 特点:
    • 面向“记录”或“块”:它以“size * nmemb”大小的块为单位进行操作。
    • 缓冲:性能通常更高,因为它减少了直接的系统调用次数。
    • 格式无关:它也是直接读写二进制数据,但常与 fwrite 配合使用来读写结构化数据。

read (以Linux/Unix为例)

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
  • 参数:
    • int fd: 文件描述符,一个非负整数,唯一标识一个打开的文件、管道、套接字等,通过 open() 函数获得。
    • void *buf: 指向一个内存缓冲区的指针,用于存放读取到的数据。
    • size_t count: 你希望读取的字节数
  • 返回值:
    • 成功时,返回实际读取到的字节数
    • 如果到达文件末尾,返回 0
    • 如果发生错误,返回 -1,并设置 errno 来表示具体的错误。
  • 特点:
    • 面向“字节流”:它以字节为单位进行操作。
    • 无缓冲:直接与内核交互,每次调用都会触发系统调用。
    • 低级:功能更基础,更接近操作系统。

使用场景对比

特性 fread read
所属标准 C标准库 (stdio.h) POSIX标准 (unistd.h)
抽象级别 高级 底层
操作对象 FILE* 文件描述符 (int fd)
缓冲机制 有缓冲 (用户空间缓冲) 无缓冲 (内核缓冲)
性能 通常更高 (系统调用次数少) 通常较低 (每次调用都是系统调用)
可移植性 极高 (所有平台行为一致) 较低 (Windows API不同)
主要用途 读取文本文件 (fopen + fread)
读写二进制数据(如结构体)
大多数常规文件I/O
需要精确控制I/O时
网络编程 (读取套接字)
设备文件I/O
高性能场景下自行管理缓冲区

代码示例

示例 1: 使用 fread 读取文件

#include <stdio.h>
int main() {
    FILE *fp;
    char buffer[256];
    // 以二进制模式打开文件,避免平台相关的换行符转换
    fp = fopen("example.txt", "rb"); 
    if (fp == NULL) {
        perror("Error opening file");
        return 1;
    }
    // 一次读取一个 sizeof(char) 大小的数据项,共 256 个
    size_t items_read = fread(buffer, sizeof(char), 256, fp);
    if (items_read > 0) {
        printf("Successfully read %zu characters.\n", items_read);
        buffer[items_read] = '\0'; // 确保字符串正确终止
        printf("Content: %s\n", buffer);
    } else if (feof(fp)) {
        printf("Reached end of file.\n");
    } else if (ferror(fp)) {
        perror("Error reading file");
    }
    fclose(fp);
    return 0;
}

示例 2: 使用 read 读取文件 (Linux/Unix)

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int main() {
    int fd;
    char buffer[256];
    ssize_t bytes_read;
    // 打开文件,返回文件描述符
    fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Error opening file");
        return 1;
    }
    // 循环读取,直到文件末尾或出错
    while ((bytes_read = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
        buffer[bytes_read] = '\0'; // 确保字符串正确终止
        printf("Read %zd bytes. Content: %s\n", bytes_read, buffer);
    }
    if (bytes_read == 0) {
        printf("Reached end of file.\n");
    } else if (bytes_read == -1) {
        perror("Error reading file");
    }
    close(fd);
    return 0;
}

总结与选择建议

如何选择?

  1. 首选 fread

    c语言 read fread
    (图片来源网络,侵删)
    • 如果你正在进行常规的文件操作,无论是文本还是二进制,fread 几乎总是更好的选择。
    • 它的缓冲机制为你提供了更好的性能和更少的代码量。
    • 它的可移植性让你无需关心底层操作系统的差异。
  2. 考虑 read

    • 当你需要进行网络编程(通过 socket 的文件描述符读写数据)时,read 是标准做法。
    • 当你需要与特殊设备文件(如 /dev/input/mice)交互时。
    • 在一些对性能要求极致的特殊场景下,你可能想绕过C库的缓冲,自己管理内存缓冲区,这时可以直接使用 read
    • 当你编写的代码需要高度依赖POSIX特性,并且不打算移植到Windows时。

一个简单的比喻:

  • fread 就像你去超市买东西,你直接推着购物车(缓冲区)去货架取货,超市会保证货架上有货(自动填充缓冲区),你不需要关心货是从哪个仓库运来的。
  • read 就像你直接去仓库,每次只能让仓库给你搬运一小箱货物(直接系统调用)到你指定的位置,你需要自己管理货物的存放和搬运计划。

对于绝大多数应用程序来说,使用 fread 这样的高级I/O函数是更安全、更高效、更简单的方式。

c语言 read fread
(图片来源网络,侵删)
-- 展开阅读全文 --
头像
Windows沙箱如何用C语言实现安全隔离?
« 上一篇 前天
织梦网站搜索页模板代码如何修改?
下一篇 » 前天

相关文章

取消
微信二维码
支付宝二维码

目录[+]