Linux C语言read函数如何使用?

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

read 是 Linux/Unix 系统中最基本、最重要的系统调用之一,用于从文件描述符(file descriptor)中读取数据。

linxu c语言 read
(图片来源网络,侵删)

函数原型

read 函数在 <unistd.h> 头文件中声明。

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

参数详解

  1. int fd: 文件描述符

    • 这是一个非负整数,是内核为了管理一个打开的文件(或设备、套接字等)而返回的句柄。
    • 它是 open, socket, pipe, accept 等函数的返回值。
    • 当一个程序启动时,Linux 默认会打开三个文件描述符:
      • 0: 标准输入
      • 1: 标准输出
      • 2: 标准错误
    • 你可以用 0 来从键盘读取数据,用 1 向屏幕写入数据。
  2. void *buf: 缓冲区

    • 这是一个指向内存区域的指针,read 会将从 fd 读取的数据存放到这个缓冲区中。
    • 调用者必须确保这个缓冲区足够大,能够容纳最多 count 字节的数据,否则会导致缓冲区溢出,非常危险。
  3. size_t count: 读取的字节数

    linxu c语言 read
    (图片来源网络,侵删)
    • 这是你希望从 fd 读取的最大字节数。
    • read 并不保证一定能读取到 count 个字节,它可能会返回更少的字节数。

返回值

read 的返回值类型是 ssize_t,这是一个有符号的整数类型,有以下几种情况:

  • 成功时:

    • 返回实际读取到的字节数,这个值会小于或等于 count
    • 如果返回 0,表示已经到达了文件的末尾,没有更多数据可读了。
  • 出错时:

    • 返回 -1errno 会被设置为一个特定的错误码(你可以用 perror 函数打印出错误信息)。
  • 特殊情况:

    • 阻塞与非阻塞: 如果文件描述符被设置为阻塞模式(默认),并且当前没有数据可读(从网络套接字或管道读取时),read 调用会阻塞,暂停程序的执行,直到有数据到达或发生错误。
    • 如果文件描述符被设置为非阻塞模式,当没有数据可读时,read 会立即返回 -1,并将 errno 设置为 EAGAINEWOULDBLOCK

错误码

read 返回 -1 时,errno 可能被设置为以下常见值:

  • EAGAIN / EWOULDBLOCK: 文件描述符被设置为非阻塞模式,并且当前没有数据可读。
  • EBADF: fd 不是一个有效的文件描述符,或者它没有被打开为只读。
  • EINTR: 读取操作被一个信号中断。
  • EIO: 发生了 I/O 错误。
  • EINVAL: 参数 count 为 0 或负数。

一个简单的示例:从标准输入读取

这个例子演示了如何从标准输入(通常是键盘)读取用户输入,并将其打印到标准输出(通常是屏幕)。

#include <stdio.h>
#include <unistd.h> // for read
#include <string.h> // for strlen
#include <errno.h>  // for errno
#define BUFFER_SIZE 1024
int main() {
    char buffer[BUFFER_SIZE];
    ssize_t bytes_read;
    // 从标准输入 (fd=0) 读取数据,最多读取 BUFFER_SIZE-1 个字节
    // 留一个字节给字符串结束符 '\0'
    bytes_read = read(STDIN_FILENO, buffer, BUFFER_SIZE - 1);
    // 检查 read 是否出错
    if (bytes_read == -1) {
        // 使用 perror 打印一个描述性的错误信息
        perror("read error");
        return 1; // 返回非零表示错误
    }
    // 检查是否到达文件末尾 (用户按了 Ctrl+D)
    if (bytes_read == 0) {
        printf("End of input (EOF).\n");
        return 0;
    }
    // 成功读取,为字符串添加结束符 '\0'
    buffer[bytes_read] = '\0';
    // 打印读取到的内容
    printf("You entered: %s", buffer);
    printf("Number of bytes read: %zd\n", bytes_read);
    return 0;
}

如何编译和运行:

  1. 将代码保存为 read_example.c
  2. 编译:gcc read_example.c -o read_example
  3. 运行:./read_example
  4. 在终端输入一些文字,然后按回车,程序会打印你输入的内容和读取的字节数。
  5. 尝试直接按 Ctrl+D(发送 EOF 信号),程序会提示 "End of input"。

重要注意事项和最佳实践

  1. 循环读取: read 不会一次性读取你请求的所有数据,尤其是在读取网络套接字或管道时,一个健壮的程序应该在一个循环中调用 read,直到读取到期望数量的字节或到达文件末尾。

    // 一个更健壮的读取循环
    char buffer[BUFFER_SIZE];
    size_t total_bytes_to_read = 100;
    size_t total_bytes_read = 0;
    while (total_bytes_read < total_bytes_to_read) {
        ssize_t bytes_read = read(fd, buffer + total_bytes_read, total_bytes_to_read - total_bytes_read);
        if (bytes_read == -1) {
            // 错误处理
            perror("read failed");
            break;
        } else if (bytes_read == 0) {
            // EOF
            printf("EOF reached.\n");
            break;
        }
        total_bytes_read += bytes_read;
    }
  2. 缓冲区大小: count 参数是请求读取的最大字节数,而不是保证读取的字节数,不要假设 read 一定会填满你的缓冲区。

  3. 文本 vs. 二进制模式: 在 Linux 上,read 总是进行二进制模式的读取,它不会对数据进行任何转换(比如把 \r\n 转成 \n),这与 Windows 的 C 库函数(如 fread)在文本模式下有所不同,这使得 read 在处理二进制文件(如图片、可执行文件)时非常可靠。

  4. 文件指针: read 会自动更新与文件描述符 fd 相关联的“文件指针”(或叫“当前偏移量”),下一次读取会从上一次读取结束的位置继续,你可以使用 lseek 系统调用来移动这个指针。


read 与标准 I/O 库函数(fread)的区别

特性 read (系统调用) fread (C 标准库函数)
层次 底层,直接与内核交互 高层,在用户空间实现缓冲
性能 每次调用都有系统开销,适合大块数据读写 通过缓冲区减少了系统调用次数,通常更快,特别是对小文件或频繁读写
缓冲 无用户空间缓冲,数据直接在内核空间和用户空间之间传递。 有用户空间缓冲,数据先读到库的缓冲区,再由库按需填充到你的缓冲区。
可移植性 POSIX 标准,在所有 Unix-like 系统上可用 C 标准库,在几乎所有操作系统上可用
控制粒度 直接操作文件描述符,更灵活,易于与 I/O 多路复用(select, poll, epoll)结合 操作 FILE* 流,更简单,功能更丰富(如格式化 I/O printf/scanf

何时使用哪个?

  • 使用 fread / fwrite: 当你进行常规的文件读写,特别是处理文本文件或对性能要求不是极致时,这是绝大多数情况下的首选,因为它更简单、通常性能更好。
  • 使用 read / write:
    • 当你需要处理网络套接字管道终端等非文件设备时。
    • 当你进行二进制文件的精确读写,不希望有任何意外的数据转换时。
    • 当你需要与I/O 多路复用机制(如 epoll)配合使用时,因为这些机制只监控文件描述符,不监控 FILE* 流。
    • 当你在编写系统编程、驱动程序或需要极致控制性能的底层代码时。

read 是 Linux C 程序员必须掌握的核心工具,理解其行为和返回值是编写健壮、高效程序的基础。

-- 展开阅读全文 --
头像
C语言如何调用SQLServer数据库?
« 上一篇 今天
dede word图片不显示怎么办?
下一篇 » 今天

相关文章

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

目录[+]