提问人:Programmer 提问时间:9/25/2023 最后编辑:Programmer 更新时间:9/25/2023 访问量:104
C 如果文件已存在,则将数字附加到文件名
C appending number to filename if file already exists
问:
我正在尝试编写一个函数,如果该文件在我创建新文件之前已经存在,它将在文件名后面附加一个数字。这适用于客户端和服务器之间的文件传输。
例如,如果客户端发送文件名“Example.txt”,但服务器已经有一个名为“Example.txt”的文件,我想将文件名更改为“Example-1.txt”,如果已经存在,请将数字增加到“Example-2.txt”,并继续运行,直到文件名不存在。
如果文件名为“Ex.x.x.x.x.txt”或“Ex.--1.txt”,并将它们分别更改为“Ex.x.x.x.x-1.txt”和“Ex.--2.txt”,它也应该有效。
我尝试使用 strtok() 方法,但代码变得又长又乱。我尝试在网上搜索,但没有找到有关此问题的任何信息。我觉得我正试图重新发明轮子,而必须有更好的方法来解决这个问题。
编辑 - 这就是我想出的。我可以看到的一个问题是,在某个点上,当要追加的数字太大时,会出现段错误,因为我对额外的内存进行了硬编码以分配给文件路径。
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;
}
答:
以下是解决该问题的一种方法:
#ifdef __linux__
#include <unistd.h>
#define FILE_EXIST(name) (access((name),0) != -1)
#else
#include <io.h>
#define FILE_EXIST(name) (_access((name),0) != -1)
#endif
char szOutFileName[100] = "MYFILE.txt";
int tries = 0;
/* Where FILE_EXIST(char *name) is some method that tells if a filename exists or not.
** (Many ways to do).*/
while(FILE_EXIST(szOutFileName)) /* Loop breaks when we achieve a unique name...*/
sprintf(szOutFileName, "MYFILE%d.txt", tries++); /* tweak name with increment*/
读者。。。仅供参考:在DV之前,请检查OP问题和此答案的时间戳并编辑修订版。现在问题中存在的代码出现在此答案发布数小时后出现。(或者,如果必须的话,继续DV。
所以,这里有一个可能有效的原型。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
int nextName( char *cp ) {
char *sufx = strrchr( cp, '.' );
char *sp, hold[16] = {0}; // enough for ".txt" or ".html"
if( sufx )
strcpy( hold, sp = sufx );
else
sp = cp + strlen( cp );
int i = 0;
do {
struct stat sbuf;
if( stat( cp, &sbuf ) != 0 && errno == ENOENT )
return 1;
printf( "%s exists... Moving on\n", cp ); // debugging
sprintf( sp, "-%02d%s ", i, hold );
} while( ++i < 100 );
return 0;
}
int main( void ) {
char str[ 32 ] = "Example.txt";
puts( str );
if( !nextName( str ) ) {
/* Handle case where no higher version number is possible */
}
puts( str );
return 0;
}
在当前目录中创建两个文件“Example.txt”和“Example-00.txt”。这将检测它们的存在并修改文件名,直到它发现“Example-01.txt”当前不存在。等等......
如果文件名没有扩展名,这甚至应该有效。
示例:“foobar”和“foobar-01”...
注意:包含所需路径的缓冲区必须更大,以便可以将额外的 3 个字符“固定”到文件名中。
如果多个进程可能正在写入同一目录,则存在固有的争用条件。
(您可以简单地将其更改为使用每个文件的 000->999 版本。根据您的需求进行调整。
编辑:考虑到路径可以是“this.dir/filename”,需要通过以下方式改进上述内容:
/* Scan right to left examining only the filename part of a path. */
char *fndExt( char *cp ) {
char *p = cp + strlen(cp) - 1;
for( ; p >= cp && *p != '/' && *p != '\\'; p-- )
if( *p == '.' )
return p;
return NULL;
}
int nextName( char *cp ) {
char *sp = fndExt( cp, '.' );
char hold[16] = {0}; // enough for ".txt" or ".html"
if( sp )
strcpy( hold, sp );
else
sp = cp + strlen( cp );
int i = 0;
do {
/* same as above */
可能需要更大的缓冲区来解释从名称末尾起超过 30 个字符的病理性长文件名。或者,可以修改为仅从文件名末尾查找 4-5 个字符,并假定这是文件的扩展名。很大程度上取决于上下文。名为“Mr.Bar”的文件是带有扩展名的名称吗?hold[]
'.'
findExt()
'.'
评论
strrchr()
.
"ABC...DEF.txt"
strtok()
'.'
"ABC.DEF.txt"
do/while()
sprintf()
free()
memset()
filename_size + directory_size
strlen()
malloc()