read。这个函数是与 openwrite 配合使用,用于从文件描述符指向的文件中读取数据。


1. 函数介绍 Link to heading

read 是一个 Linux 系统调用,它的主要作用是从指定的文件描述符(file descriptor)所关联的文件、管道、套接字等输入流中读取数据,并将其存放到程序提供的缓冲区中。你可以把它想象成从一个“数据源”里“拿出”一些字节放到你自己的“篮子”(缓冲区)里。


2. 函数原型 Link to heading

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

3. 功能 Link to heading

尝试从文件描述符 fd 指向的文件或资源中读取最多 count 个字节的数据,并将这些数据存储到从 buf 指向的内存地址开始的缓冲区中。


4. 参数 Link to heading

  • int fd: 这是文件描述符 (file descriptor)。它是一个非负整数,由 opencreatsocket 等系统调用成功执行后返回,用以标识一个已打开的文件或资源。
  • void *buf: 这是一个指向缓冲区的指针。读取到的数据将被存放到从这个地址开始的内存空间中。你需要确保这个缓冲区至少有 count 个字节的空间。
  • size_t count: 这是你希望读取的最大字节数。函数不会读取超过 count 个字节的数据。

5. 返回值 Link to heading

read 函数返回实际读取到的字节数,这个数可能小于或等于请求的 count

  • 成功时:
    • 返回实际读取到的字节数 (0 <= 返回值 <= count)。
    • 如果返回值为 0,通常表示已经到达文件末尾 (End Of File, EOF) 或者没有更多数据可读(例如从管道或网络套接字读取时对端已关闭)。
  • 出错时:
    • 返回 -1,并设置全局变量 errno 来指示具体的错误类型(例如 EAGAINEBADFEFAULT 等)。

6. 相似函数,或关联函数 Link to heading

  • pread: 类似于 read,但它允许你在一次调用中同时指定要读取的文件描述符、缓冲区、读取字节数以及文件内的偏移量。它不会改变文件的当前偏移量。
  • readv: 允许你将数据读入到多个不连续的缓冲区(一个 iovec 数组)中,这在处理结构化数据时很有用。
  • write: 与 read 相反,用于将数据写入到文件描述符指向的文件或资源中。
  • open: 通常在调用 read 之前使用,用来获取文件描述符。

7. 示例代码 Link to heading

示例 1:从普通文件读取内容 Link to heading

#include <unistd.h>  // read, write, close
#include <fcntl.h>   // open, O_RDONLY
#include <stdio.h>   // perror, printf
#include <stdlib.h>  // exit
#include <errno.h>   // errno

#define BUFFER_SIZE 1024

int main() {
    int fd;                 // 文件描述符
    char buffer[BUFFER_SIZE]; // 用于存储读取数据的缓冲区
    ssize_t bytes_read;     // 实际读取的字节数

    // 1. 打开一个文件以供读取 (假设当前目录下有名为 "example.txt" 的文件)
    fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("Error opening file"); // 打印错误信息
        exit(EXIT_FAILURE);
    }

    printf("File opened successfully with fd: %d\n", fd);

    // 2. 循环读取文件内容,直到文件末尾
    while ((bytes_read = read(fd, buffer, BUFFER_SIZE)) > 0) {
        // 3. 将读取到的内容写入标准输出 (屏幕)
        // 注意:这里没有处理 write 可能只写入部分数据的情况,
        // 在实际应用中可能需要循环确保所有数据都被写入。
        if (write(STDOUT_FILENO, buffer, bytes_read) != bytes_read) {
             perror("Error writing to stdout");
             close(fd); // 出错也要记得关闭文件
             exit(EXIT_FAILURE);
        }
        // 可选:打印本次读取了多少字节
        // printf("Read %zd bytes\n", bytes_read);
    }

    // 4. 检查 read 是否因错误而返回
    if (bytes_read == -1) {
        perror("Error reading file");
        close(fd);
        exit(EXIT_FAILURE);
    }

    // 5. 关闭文件描述符
    if (close(fd) == -1) {
        perror("Error closing file");
        exit(EXIT_FAILURE);
    }

    printf("\nFinished reading file.\n");
    return 0;
}

代码解释:

  1. 定义了缓冲区大小 BUFFER_SIZE
  2. 使用 open() 以只读模式 (O_RDONLY) 打开名为 example.txt 的文件。如果失败,perror() 会打印具体错误原因,程序退出。
  3. 进入 while 循环,调用 read(fd, buffer, BUFFER_SIZE)。它会尝试读取最多 1024 个字节。
  4. 如果 read 返回大于 0 的值,说明读到了数据。然后使用 write() 将这些数据写到标准输出 (STDOUT_FILENO,其值为 1)。
  5. 循环继续,直到 read 返回 0(EOF)。
  6. 检查 read 是否返回 -1(错误)。
  7. 最后,使用 close() 关闭文件描述符,释放资源。

示例 2:从标准输入读取一行 Link to heading

这个例子演示如何从键盘(标准输入)读取用户输入的一行文本。

#include <unistd.h>  // read
#include <stdio.h>   // perror, printf
#include <stdlib.h>  // exit

#define MAX_INPUT_SIZE 256

int main() {
    char input_buffer[MAX_INPUT_SIZE];
    ssize_t bytes_read;
    int i;

    printf("Enter a line of text (max %d chars): ", MAX_INPUT_SIZE - 1);

    // 从标准输入 (STDIN_FILENO=0) 读取数据
    bytes_read = read(STDIN_FILENO, input_buffer, MAX_INPUT_SIZE - 1);

    if (bytes_read == -1) {
        perror("Error reading from stdin");
        exit(EXIT_FAILURE);
    } else if (bytes_read == 0) {
        printf("No data read (EOF on stdin?)\n");
    } else {
        // 寻找换行符 \n,将其替换为字符串结束符 \0
        // 这样可以方便地将输入作为 C 字符串处理
        for (i = 0; i < bytes_read; ++i) {
            if (input_buffer[i] == '\n') {
                input_buffer[i] = '\0';
                break;
            }
        }
        // 如果没有找到 \n (例如输入达到缓冲区最大限制),手动添加 \0
        if (i == bytes_read) {
             input_buffer[bytes_read] = '\0';
        }

        printf("You entered: %s\n", input_buffer);
        // 注意:实际读取的字符数可能比 strlen(input_buffer) 多1(因为 \n 被替换了)
        printf("Number of bytes read (including \\n if present): %zd\n", bytes_read);
    }

    return 0;
}

代码解释:

  1. 提示用户输入。
  2. 调用 read(STDIN_FILENO, ...) 从键盘读取最多 MAX_INPUT_SIZE - 1 个字符(留一个位置给可能添加的 \0)。
  3. 检查返回值:-1 表示错误,0 表示 EOF,正数表示读取的字节数。
  4. 为了方便打印,代码查找读入的缓冲区中的换行符 \n 并将其替换为字符串结束符 \0
  5. 使用 printf 打印处理后的字符串和实际读取的字节数。

理解 read 函数的关键在于掌握文件描述符的概念以及如何处理其返回值(特别是区分读到数据、到达文件末尾和发生错误的情况)。