You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用read(2)、write(2)等底层系统调用获取标准输入与文件字节数

用read(2)/write(2)统计stdin与文件的字节数

嘿,这个问题我太懂了!确实,lseek(2)对管道、终端这类非可定位流(比如标准输入stdin)完全没用——因为它们的内容是流式传输的,没有固定的“文件末尾”让你跳转过去。不过你想到用read(2)循环读取来统计字节数的思路完全正确,我来给你细化一下具体实现,不管是普通文件还是stdin都能通用。

核心逻辑

不管输入是普通文件还是stdin,通用的处理方式就是:

  • 用一块合适大小的缓冲区(别逐字节读,效率太低)循环调用read(2)
  • 每次读取后累加返回的字节数,直到read(2)返回0(表示读到EOF)
  • 注意处理read(2)的错误情况,比如被信号中断的情况不算真正的失败

完整代码示例

下面是一个C程序,既可以处理命令行传入的文件,也可以直接读取stdin(当没有命令行参数时):

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#define BUF_SIZE 4096  // 用系统页大小作为缓冲区,平衡效率与内存占用

int main(int argc, char *argv[]) {
    int fd;
    ssize_t bytes_read;
    unsigned char buf[BUF_SIZE];
    unsigned long long total_bytes = 0;
    unsigned char *content = NULL;  // 如果需要存储内容,用动态数组

    // 确定输入源:有参数则打开文件,否则用stdin
    if (argc > 1) {
        fd = open(argv[1], O_RDONLY);
        if (fd == -1) {
            perror("Failed to open file");
            exit(EXIT_FAILURE);
        }
    } else {
        fd = STDIN_FILENO;  // 等价于0,用宏更清晰可读
    }

    // 循环读取直到EOF或错误
    while ((bytes_read = read(fd, buf, BUF_SIZE)) > 0) {
        total_bytes += bytes_read;

        // 如果你需要把内容存入数组,这里处理动态扩容与复制
        content = realloc(content, total_bytes);
        if (content == NULL) {
            perror("Failed to allocate memory");
            if (argc > 1) close(fd);
            exit(EXIT_FAILURE);
        }
        memcpy(content + total_bytes - bytes_read, buf, bytes_read);
    }

    // 处理read的错误情况
    if (bytes_read == -1) {
        // EINTR是被信号中断,可忽略并继续读取;其他情况才报错
        if (errno != EINTR) {
            perror("Failed to read data");
            free(content);
            if (argc > 1) close(fd);
            exit(EXIT_FAILURE);
        }
    }

    // 输出结果
    printf("Total bytes: %llu\n", total_bytes);

    // 清理资源
    free(content);
    if (argc > 1) close(fd);

    return EXIT_SUCCESS;
}

关键细节说明

  • 缓冲区大小选择:用4096字节是因为这是Linux系统的默认页大小,每次读取一页数据能减少系统调用的次数,大幅提升效率——逐字节读取的话,每读一个字节都要触发一次系统调用,开销极大。
  • 处理EINTR错误:当read(2)被外部信号打断时,会返回-1并设置errno=EINTR,这种情况不是真正的读取失败,应该继续尝试读取。
  • 动态数组存储内容:如果需要把输入内容存入数组,不能一开始就分配固定大小,必须用realloc动态扩容,每次读取后调整数组大小并复制缓冲区内容。
  • 兼容非可定位流:这个程序对stdin(比如cat file | ./your_program或者直接在终端输入)、普通文件都能正常工作,而lseek(2)只能处理普通文件。

内容的提问来源于stack exchange,提问作者Nicole

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:25:16