如何用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
相关产品推荐
相关产品推荐

