如何使用系统调用write()不删除文件数据并实现内容插入?
解决方案:用
write()在文件中间插入内容而不覆盖原有数据 这是个很典型的系统调用使用误区——write()本身并不支持「插入」操作,它只会从当前文件偏移量开始覆盖写入。你看到的Hello bad w就是因为新内容把原文件中从插入点开始的字符直接覆盖了。要实现真正的插入,得手动把插入点后面的内容先挪走,具体步骤和代码如下:
核心思路
底层文件系统没有「插入」的原生操作,所以我们需要手动完成这几个步骤:
- 以可读可写模式打开目标文件
- 定位到要插入的位置
- 读取插入点之后的所有内容并暂存
- 回退到插入位置,写入新内容
- 把暂存的原有内容追加到新内容之后
完整代码示例
#include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> int main() { const char *filename = "test.txt"; off_t insert_pos = 6; // 对应"Hello "之后的位置(共6个字符) const char *insert_str = "bad "; // 末尾加空格让最终结果为"Hello bad World" // 1. 打开文件:可读可写模式 int fd = open(filename, O_RDWR); if (fd == -1) { perror("open failed"); return EXIT_FAILURE; } // 2. 获取文件总大小,计算插入点后的剩余内容长度 off_t file_size = lseek(fd, 0, SEEK_END); if (file_size == -1) { perror("lseek get file size failed"); close(fd); return EXIT_FAILURE; } off_t remaining_len = file_size - insert_pos; // 3. 分配缓冲区,读取插入点后的所有内容 char *buf = malloc(remaining_len + 1); // +1 是为了给字符串结束符留位置(可选,方便调试) if (!buf) { perror("malloc failed"); close(fd); return EXIT_FAILURE; } // 回退到插入位置,读取剩余内容 if (lseek(fd, insert_pos, SEEK_SET) == -1) { perror("lseek to insert pos failed"); free(buf); close(fd); return EXIT_FAILURE; } ssize_t read_bytes = read(fd, buf, remaining_len); if (read_bytes != remaining_len) { perror("read failed or incomplete"); free(buf); close(fd); return EXIT_FAILURE; } buf[read_bytes] = '\0'; // 可选,方便打印调试内容 // 4. 再次回退到插入位置,写入新内容 if (lseek(fd, insert_pos, SEEK_SET) == -1) { perror("lseek to insert pos failed again"); free(buf); close(fd); return EXIT_FAILURE; } ssize_t write_bytes = write(fd, insert_str, sizeof("bad ") - 1); // 减去字符串自带的'\0' if (write_bytes != sizeof("bad ") - 1) { perror("write insert string failed"); free(buf); close(fd); return EXIT_FAILURE; } // 5. 写入之前暂存的原有内容 write_bytes = write(fd, buf, remaining_len); if (write_bytes != remaining_len) { perror("write remaining content failed"); free(buf); close(fd); return EXIT_FAILURE; } // 清理资源 free(buf); close(fd); printf("插入完成!\n"); return EXIT_SUCCESS; }
关键细节说明
- 文件打开模式:必须用
O_RDWR,这样才能同时完成读和写操作;如果是新文件可以加O_CREAT,但你的场景是操作已有文件,无需额外添加。 lseek()的使用:用来移动文件偏移量,SEEK_SET表示从文件开头计算位置,SEEK_END表示跳到文件末尾获取总大小。- 缓冲区分配:如果文件很大,一次性读取所有剩余内容可能占用过多内存,这时候可以分块读取/写入,避免内存溢出。
- 错误处理:每个系统调用(
open/lseek/read/write/malloc)都要检查返回值,避免出现隐性错误。 - 多进程/线程场景:如果有多个进程同时操作该文件,需要加文件锁(比如
flock()或fcntl()的记录锁)来避免竞态条件。
内容的提问来源于stack exchange,提问作者Nelson Sousa
相关产品推荐
相关产品推荐

