C语言实现批量替换TXT文件指定字符串为大写的问题求助
问题分析与解决方案
你的代码存在几个关键问题导致无法完成替换需求,逐一修正后就能实现功能:
核心问题梳理
- 直接修改
argv[1]:argv指向的内存通常是只读的,修改会引发未定义行为,且会破坏原始搜索词,导致后续匹配失效。 - 仅匹配完整单词:
strcmp是完全匹配整个buf,但你需要的是子串替换(比如把applesauce里的apple替换成APPLE)。 - 丢失文件格式:
fscanf("%s", buf)会跳过空格、换行等分隔符,写入临时文件时无法保留原文件的格式。 - 未完成文件替换流程:没有将临时文件的内容写回原文件,也没处理文件打开失败的情况。
- 未遍历子目录:当前代码只处理当前目录的
.txt文件,没递归遍历子目录。
修正后的完整代码
#include <dirent.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include <unistd.h> #define BUFFER_SIZE 1000 // 递归遍历目录处理所有.txt文件 void process_directory(const char *dir_path, const char *target_word) { DIR *d = opendir(dir_path); if (!d) { perror("Failed to open directory"); return; } struct dirent *dir_entry; char full_path[BUFFER_SIZE]; size_t target_len = strlen(target_word); // 生成大写版本的目标词 char *upper_target = malloc(target_len + 1); if (!upper_target) { perror("Failed to allocate memory"); closedir(d); return; } for (size_t i = 0; i < target_len; i++) { upper_target[i] = toupper((unsigned char)target_word[i]); } upper_target[target_len] = '\0'; while ((dir_entry = readdir(d)) != NULL) { // 跳过.和..目录 if (strcmp(dir_entry->d_name, ".") == 0 || strcmp(dir_entry->d_name, "..") == 0) { continue; } // 拼接完整文件/目录路径 snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, dir_entry->d_name); // 如果是目录,递归处理 if (dir_entry->d_type == DT_DIR) { process_directory(full_path, target_word); } // 如果是.txt文件,处理替换 else if (dir_entry->d_type == DT_REG) { size_t name_len = strlen(dir_entry->d_name); if (name_len >= 4 && strcmp(dir_entry->d_name + name_len - 4, ".txt") == 0) { FILE *src_fp = fopen(full_path, "r"); if (!src_fp) { perror("Failed to open source file"); continue; } // 创建临时文件,放在同一目录下避免跨分区问题 char temp_path[BUFFER_SIZE]; snprintf(temp_path, sizeof(temp_path), "%s.tmp", full_path); FILE *temp_fp = fopen(temp_path, "w"); if (!temp_fp) { perror("Failed to open temp file"); fclose(src_fp); continue; } char buffer[BUFFER_SIZE]; while (fgets(buffer, sizeof(buffer), src_fp) != NULL) { char *pos = buffer; // 循环查找目标子串 while ((pos = strstr(pos, target_word)) != NULL) { // 写入子串之前的内容 fwrite(buffer, 1, pos - buffer, temp_fp); // 写入大写版本的目标词 fwrite(upper_target, 1, target_len, temp_fp); // 移动指针到当前匹配位置之后 pos += target_len; } // 写入剩余的内容 fwrite(pos, 1, strlen(pos), temp_fp); } // 关闭文件 fclose(src_fp); fclose(temp_fp); // 替换原文件(需要确保临时文件写入成功) if (remove(full_path) != 0) { perror("Failed to remove original file"); remove(temp_path); continue; } if (rename(temp_path, full_path) != 0) { perror("Failed to rename temp file"); remove(temp_path); } printf("Processed: %s\n", full_path); } } } free(upper_target); closedir(d); } int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "Usage: %s <target-word>\n", argv[0]); return 1; } printf("Target word: %s\n", argv[1]); printf("Searching and replacing in current directory and subdirectories...\n"); process_directory(".", argv[1]); return 0; }
关键修正说明
- 递归遍历目录:新增
process_directory函数,递归处理子目录,跳过.和..避免死循环。 - 子串替换逻辑:使用
strstr查找目标子串,分段写入临时文件——先写子串前的内容,再写大写版本的目标词,最后写剩余内容。 - 安全处理内存与文件:
- 单独分配内存存储大写版本的目标词,不修改
argv[1]。 - 检查文件打开、内存分配是否成功,避免崩溃。
- 临时文件放在原文件同一目录,避免跨分区
rename失败。
- 单独分配内存存储大写版本的目标词,不修改
- 保留文件格式:用
fgets按行读取,保留原文件的换行、空格等格式。 - 文件替换流程:先写入临时文件,确认成功后删除原文件,再将临时文件重命名为原文件名,避免数据丢失。
内容的提问来源于stack exchange,提问作者giotto1
相关产品推荐
相关产品推荐

