C语言:文件已存在时为文件名追加数字的实现方法问询
文件名重复处理问题及解决方案
需求概述
- 创建文件前检查目标路径是否存在,若存在则在文件名的扩展名前追加递增数字,直到找到不存在的文件名
- 示例场景:
- 原文件名
Example.txt→ 存在时改为Example-1.txt,仍存在则继续递增为Example-2.txt - 复杂文件名处理:
Ex.x.x.x.x.txt→Ex.x.x.x.x-1.txtEx.--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; }
现有代码的核心问题
- 硬编码内存大小:固定
+64的预留空间无法容纳大数字的字符串表示(比如数字达到百万级时,字符串长度会超过预留值),导致sprintf缓冲区溢出,触发段错误 - 字符串拼接不安全:使用
strcat/strncat存在潜在溢出风险,且没有处理无扩展名的文件名场景 - 内存泄漏风险:原
*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
相关产品推荐
相关产品推荐

