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

C语言:文件已存在时为文件名追加数字的实现方法问询

文件名重复处理问题及解决方案

需求概述

  • 创建文件前检查目标路径是否存在,若存在则在文件名的扩展名前追加递增数字,直到找到不存在的文件名
  • 示例场景:
    • 原文件名Example.txt → 存在时改为Example-1.txt,仍存在则继续递增为Example-2.txt
    • 复杂文件名处理:
      • Ex.x.x.x.x.txt → Ex.x.x.x.x-1.txt
      • Ex.--1.txt → Ex.--2.txt
  • 曾尝试用strtok()实现,但代码冗余混乱;现有代码因硬编码内存大小,在数字过大时出现段错误

现有问题代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

void check_if_file_exists(char **filepath, const char *filename, const char *directory)
{
    struct stat file_status;

    if (stat(*filepath, &file_status) == 0)
    {
        char *final_filepath, *new_filepath, *filename_extension;
        size_t filepath_size;
        int append;

        filepath_size = strlen(filename) + strlen(directory) + 64;
        append = 1;

        final_filepath = safe_malloc(filepath_size);
        new_filepath = safe_malloc(filepath_size);

        strcpy(new_filepath, directory);

        filename_extension = strrchr(filename, '.');

        strncat(new_filepath, filename, filename_extension - filename);
        strcat(new_filepath, "-%d");
        if (filename_extension != NULL)
        {
            strcat(new_filepath, filename_extension);
        }

        do
        {
            sprintf(final_filepath, new_filepath, append++);

        } while (stat(final_filepath, &file_status) == 0);

        *filepath = final_filepath;

        free(new_filepath);
    }
}

void *safe_malloc(size_t size)
{
    void *ptr = malloc(size);

    if (!ptr && size > 0)
    {
        perror("Malloc failed\n");
        exit(EXIT_FAILURE);
    }

    return ptr;
}

现有代码的核心问题

  1. 硬编码内存大小:固定+64的预留空间无法容纳大数字的字符串表示(比如数字达到百万级时,字符串长度会超过预留值),导致sprintf缓冲区溢出,触发段错误
  2. 字符串拼接不安全:使用strcat/strncat存在潜在溢出风险,且没有处理无扩展名的文件名场景
  3. 内存泄漏风险:原*filepath的内存未被释放,会造成泄漏

改进后的代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

void *safe_malloc(size_t size)
{
    void *ptr = malloc(size);
    if (!ptr && size > 0)
    {
        perror("Malloc failed");
        exit(EXIT_FAILURE);
    }
    return ptr;
}

void check_if_file_exists(char **filepath, const char *filename, const char *directory)
{
    struct stat file_status;

    // 初始文件不存在,直接返回原路径
    if (stat(*filepath, &file_status) != 0)
    {
        return;
    }

    char *name_body = NULL;
    const char *ext = "";
    size_t name_len = strlen(filename);

    // 拆分文件名主体与扩展名:跳过开头的'.'(隐藏文件)
    char *last_dot = strrchr(filename, '.');
    if (last_dot != NULL && last_dot != filename)
    {
        size_t body_len = last_dot - filename;
        name_body = safe_malloc(body_len + 1);
        strncpy(name_body, filename, body_len);
        name_body[body_len] = '\0';
        ext = last_dot; // 包含'.'的完整扩展名
    }
    else
    {
        // 无有效扩展名,整个文件名作为主体
        name_body = safe_malloc(name_len + 1);
        strcpy(name_body, filename);
    }

    int append = 1;
    char *final_filepath = NULL;
    size_t dir_len = strlen(directory);

    do
    {
        // 动态计算当前数字的字符串长度
        int num_str_len = snprintf(NULL, 0, "%d", append);
        // 计算所需总缓冲区大小:目录+主体+"-"+数字+扩展名+终止符
        size_t buf_size = dir_len + strlen(name_body) + 1 + num_str_len + strlen(ext) + 1;

        // 释放上一次迭代的内存
        if (final_filepath != NULL)
        {
            free(final_filepath);
        }
        final_filepath = safe_malloc(buf_size);

        // 安全拼接路径
        snprintf(final_filepath, buf_size, "%s%s-%d%s", directory, name_body, append++, ext);

    } while (stat(final_filepath, &file_status) == 0);

    // 更新路径并清理内存
    free(*filepath);
    *filepath = final_filepath;
    free(name_body);
}

改进说明

  • 动态内存分配:每次迭代根据当前数字的长度计算所需缓冲区大小,彻底解决大数字导致的溢出问题
  • 安全字符串操作:用snprintf替代strcat/sprintf,从根源避免缓冲区溢出
  • 完善的文件名处理:正确识别隐藏文件(如.gitignore),不会误将开头的.当作扩展名
  • 内存泄漏修复:释放原*filepath的内存,避免资源泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:05:19