You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修正说明

  1. 递归遍历目录:新增process_directory函数,递归处理子目录,跳过.和..避免死循环。
  2. 子串替换逻辑:使用strstr查找目标子串,分段写入临时文件——先写子串前的内容,再写大写版本的目标词,最后写剩余内容。
  3. 安全处理内存与文件:
    • 单独分配内存存储大写版本的目标词,不修改argv[1]。
    • 检查文件打开、内存分配是否成功,避免崩溃。
    • 临时文件放在原文件同一目录,避免跨分区rename失败。
  4. 保留文件格式:用fgets按行读取,保留原文件的换行、空格等格式。
  5. 文件替换流程:先写入临时文件,确认成功后删除原文件,再将临时文件重命名为原文件名,避免数据丢失。

内容的提问来源于stack exchange,提问作者giotto1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 14:15:40