用C语言修改文本文件指定行时内容覆盖问题的解决建议
解决直接修改文件时后续行内容被覆盖的问题
问题根源
直接在原文件上原地修改内容时,若修改后的行长度与原行不一致,会导致文件指针位置偏移,后续字节被错误覆盖。比如原行file:=good\n(假设含换行符共10字节),替换为file:=newfile\n(共12字节)后,多出来的2个字节会覆盖下一行的前两个字符sr,最终导致src:=$(file).c变成:=$(file).c。
修正方案(不创建临时文件)
直接原地修改难以处理行长度变化的情况,可靠的做法是先将整个文件内容读取到内存,在内存中完成修改后再写回原文件。以下是具体实现代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> // 用于ftruncate #define BUFFER_SIZE 1024 int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <new_file_name>\n", argv[0]); return 1; } const char *filename = "dummyFile.txt"; FILE *fp = fopen(filename, "r+"); if (!fp) { perror("Failed to open file"); return 1; } // 1. 读取整个文件到内存 char *content = NULL; size_t total_size = 0; char buffer[BUFFER_SIZE]; size_t bytes_read; while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, fp)) > 0) { char *temp = realloc(content, total_size + bytes_read + 1); if (!temp) { perror("Failed to allocate memory"); free(content); fclose(fp); return 1; } content = temp; memcpy(content + total_size, buffer, bytes_read); total_size += bytes_read; } content[total_size] = '\0'; // 添加字符串结束符 // 2. 定位并修改目标行 const char *target_prefix = "file:="; char *line_start = strstr(content, target_prefix); if (!line_start) { fprintf(stderr, "Target line not found\n"); free(content); fclose(fp); return 1; } // 找到行尾(换行符或文件末尾) char *line_end = strchr(line_start, '\n'); if (!line_end) line_end = content + total_size; // 构造新行 char new_line[BUFFER_SIZE]; snprintf(new_line, sizeof(new_line), "%s%s\n", target_prefix, argv[1]); int len_diff = strlen(new_line) - (line_end - line_start); // 根据长度差调整内存内容 if (len_diff > 0) { // 需要扩展内存,移动后续内容 char *temp = realloc(content, total_size + len_diff + 1); if (!temp) { perror("Failed to reallocate memory"); free(content); fclose(fp); return 1; } content = temp; // 重新定位行起始和结束位置(内存地址可能变化) line_start = strstr(content, target_prefix); line_end = strchr(line_start, '\n'); if (!line_end) line_end = content + total_size; memmove(line_end + len_diff, line_end, content + total_size - line_end); total_size += len_diff; } else if (len_diff < 0) { // 缩小内容,移动后续内容覆盖多余部分 memmove(line_end + len_diff, line_end, content + total_size - line_end); total_size += len_diff; } // 写入新行 memcpy(line_start, new_line, strlen(new_line)); content[total_size] = '\0'; // 3. 将修改后的内容写回文件并截断 fseek(fp, 0, SEEK_SET); fwrite(content, 1, total_size, fp); ftruncate(fileno(fp), total_size); // 截断多余的旧内容 // 清理资源 free(content); fclose(fp); return 0; }
关键说明
- 内存中处理内容可以彻底避免原地修改的长度偏移问题,所有修改都在内存缓冲区完成
- 通过计算新行与原行的长度差,移动后续内容来适配长度变化,确保所有行的位置正确
- 使用
ftruncate截断文件,避免原文件长度大于新内容时末尾残留旧数据 - 适用于普通大小的makefile文件,若处理超大文件,需考虑分块读取修改的方案
内容的提问来源于stack exchange,提问作者John Doe
相关产品推荐
相关产品推荐

