C 如果文件已存在,则将数字附加到文件名

C appending number to filename if file already exists

提问人:Programmer 提问时间:9/25/2023 最后编辑:Programmer 更新时间:9/25/2023 访问量:104

问:

我正在尝试编写一个函数,如果该文件在我创建新文件之前已经存在,它将在文件名后面附加一个数字。这适用于客户端和服务器之间的文件传输。

例如,如果客户端发送文件名“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;
}
C 文件 服务器 客户端 strtok

评论

1赞 Fe2O3 9/25/2023
你看过 doco 吗?该函数名称的中间有两个“r”。strrchr()
1赞 Retired Ninja 9/25/2023
从文件名的末尾开始,找到最后一个 .将没有该部分的名称保存在某处,并在 1 处开始计数器。使用您保存的基数和计数器构造新名称,并检查服务器。重复最后一步,直到服务器说可以增加每次失败的计数。.
1赞 Fe2O3 9/25/2023
英勇的努力......但。。。如果文件名是 ? 将那些连续的实例视为一个实例。文件名将更改为 ...这对您的应用重要吗?"ABC...DEF.txt"strtok()'.'"ABC.DEF.txt"
1赞 Fe2O3 9/25/2023
新代码可能无法处理没有扩展名的文件名。(例如:“报告”)。考虑避免重复......调用函数需要知道是否分配缓冲区...没必要...可以直接替换为适当的 ',并且没有两个变量。否则,看起来还不错......do/while()sprintf()free()memset()filename_size + directory_sizestrlen()
1赞 Fe2O3 9/25/2023
对于长时间运行的进程(如服务器)来说,特别重要的是没有内存泄漏......侵蚀可用的堆内存将起作用...直到它没有。验证 的返回值也很重要。我应该早点提到这一点。malloc()

答:

0赞 greg spears 9/25/2023 #1

以下是解决该问题的一种方法:

#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*/
1赞 Fe2O3 9/25/2023 #2

读者。。。仅供参考:在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()'.'