高级I/O版chunk.c改低级系统调用后分片异常,求修复
定位并修复低级系统调用版文件拆分程序的问题
常见问题根源
低级系统调用(如read/write/open)是无缓冲的,和高级I/O(fgets/fprintf)的行处理逻辑完全不同,只生成第一个分片通常是以下原因:
- 未处理不完整行:一次
read可能只读到半行,直接计数换行符会导致后续行统计错误,误以为文件已结束。 - 分片切换逻辑错误:写完第一个分片后未关闭当前文件描述符,也未正确打开下一个分片文件。
- 循环终止条件错误:没有处理
read返回部分数据(非EOF)的情况,提前退出循环。
修复后的完整实现
#include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define BUFFER_SIZE 4096 #define PART_FMT "part-%02d" int main(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "Usage: %s <input-file> <lines-per-part>\n", argv[0]); exit(EXIT_FAILURE); } const char *input_path = argv[1]; int lines_per_part = atoi(argv[2]); if (lines_per_part <= 0) { fprintf(stderr, "Invalid lines per part: %d\n", lines_per_part); exit(EXIT_FAILURE); } int input_fd = open(input_path, O_RDONLY); if (input_fd == -1) { perror("Failed to open input file"); exit(EXIT_FAILURE); } int part_num = 0; int line_count = 0; int output_fd = -1; char buffer[BUFFER_SIZE]; ssize_t bytes_read; char *buf_ptr, *newline_ptr; size_t total_bytes, remain_bytes = 0; // 初始化第一个分片文件 char part_path[256]; snprintf(part_path, sizeof(part_path), PART_FMT, part_num); output_fd = open(part_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (output_fd == -1) { perror("Failed to open first part file"); close(input_fd); exit(EXIT_FAILURE); } while ((bytes_read = read(input_fd, buffer + remain_bytes, BUFFER_SIZE - remain_bytes)) > 0) { total_bytes = bytes_read + remain_bytes; buf_ptr = buffer; while (buf_ptr < buffer + total_bytes) { newline_ptr = memchr(buf_ptr, '\n', buffer + total_bytes - buf_ptr); if (!newline_ptr) { // 缓存未处理的半行数据到缓冲区头部 memmove(buffer, buf_ptr, buffer + total_bytes - buf_ptr); remain_bytes = buffer + total_bytes - buf_ptr; break; } // 写入当前完整行 size_t line_len = newline_ptr - buf_ptr + 1; if (write(output_fd, buf_ptr, line_len) != line_len) { perror("Write to part file failed"); close(input_fd); close(output_fd); exit(EXIT_FAILURE); } line_count++; buf_ptr = newline_ptr + 1; // 达到行数阈值,切换分片 if (line_count == lines_per_part) { close(output_fd); part_num++; line_count = 0; snprintf(part_path, sizeof(part_path), PART_FMT, part_num); output_fd = open(part_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (output_fd == -1) { perror("Open new part file failed"); close(input_fd); exit(EXIT_FAILURE); } } } // 缓冲区已完全处理,重置剩余字节数 if (buf_ptr >= buffer + total_bytes) { remain_bytes = 0; } } // 处理文件末尾的剩余数据(无换行符的内容) if (remain_bytes > 0 && output_fd != -1) { if (write(output_fd, buffer, remain_bytes) != remain_bytes) { perror("Write remaining data failed"); close(input_fd); close(output_fd); exit(EXIT_FAILURE); } } // 清理所有文件描述符 close(input_fd); if (output_fd != -1) { close(output_fd); } return EXIT_SUCCESS; }
关键修复点说明
- 半行数据缓存:每次读取后如果找不到完整换行符,将剩余数据移到缓冲区头部,下次读取时拼接,保证行统计准确。
- 分片切换逻辑:达到指定行数后,立即关闭当前输出文件,重新打开新的分片文件,同时重置计数器和分片编号。
- 部分读取处理:循环调用
read直到返回0(EOF),确保读取完所有数据,避免提前退出。 - 资源安全:所有打开的文件描述符都在程序结束前关闭,避免资源泄漏。
内容的提问来源于stack exchange,提问作者springbook
相关产品推荐
相关产品推荐

