这是获取Python文件名唯一版本的最佳方式吗?

2024-04-29 20:59:18 发布

您现在位置:Python中文网/ 问答频道 /正文

仍然“潜入”Python,并想确保我没有忽视的东西。我编写了一个脚本,从几个zip文件中提取文件,并将提取的文件一起保存在一个目录中。为了防止重复的文件名被重写,我编写了这个小函数——我只是想知道是否有更好的方法来做到这一点? 谢谢!

def unique_filename(file_name):
counter = 1
file_name_parts = os.path.splitext(file_name) # returns ('/path/file', '.ext')
while os.path.isfile(file_name): 
    file_name = file_name_parts[0] + '_' + str(counter) + file_name_parts[1]
    counter += 1
return file_name

我真的需要文件在一个目录中,在我的情况下,编号重复是绝对可以接受的,所以我不想寻找一个更健壮的方法(我想任何指针都是受欢迎的),但只是为了确保这是正确的方式来完成。


Tags: 文件path方法函数name目录脚本os
3条回答

两个小变化。。。

base_name, ext = os.path.splitext(file_name) 

你得到两个意义不同的结果,给它们起不同的名字。

file_name = "%s_%d%s" % (base_name, str(counter), ext)

它不会更快或更短。但是,当您想要更改文件名模式时,该模式位于一个位置,使用起来稍微容易一些。

一个问题是上面的代码中存在竞争条件,因为在测试存在性和创建文件之间存在差距。这可能会涉及到安全问题(想想有人恶意地将符号链接插入到他们无法覆盖的敏感文件中,但您的程序可以使用更高的权限运行)这样的攻击正是不赞成使用os.tempnam()这样的攻击的原因。

要解决这个问题,最好的方法是实际尝试以这样的方式创建文件:如果失败,您将得到一个异常;如果成功,则返回实际打开的文件对象。这可以通过传递os.O CREAT和os.O EXCL标志,使用较低级别的os.open函数来完成。打开后,返回您创建的实际文件(以及可选的文件名)。下面是修改后的代码,以使用此方法(返回(文件,文件名)元组):

def unique_file(file_name):
    counter = 1
    file_name_parts = os.path.splitext(file_name) # returns ('/path/file', '.ext')
    while 1:
        try:
            fd = os.open(file_name, os.O_CREAT | os.O_EXCL | os.O_RDRW)
            return os.fdopen(fd), file_name
        except OSError:
            pass
        file_name = file_name_parts[0] + '_' + str(counter) + file_name_parts[1]
        counter += 1

[编辑]实际上,更好的方法是使用tempfile模块,尽管您可能会失去对命名的一些控制,但这样可以为您解决上述问题。下面是一个使用它的示例(保持类似的接口):

def unique_file(file_name):
    dirname, filename = os.path.split(file_name)
    prefix, suffix = os.path.splitext(filename)

    fd, filename = tempfile.mkstemp(suffix, prefix+"_", dirname)
    return os.fdopen(fd), filename

>>> f, filename=unique_file('/home/some_dir/foo.txt')
>>> print filename
/home/some_dir/foo_z8f_2Z.txt

这种方法的唯一缺点是,您将始终获得一个文件名,其中包含一些随机字符,因为不会尝试首先创建未修改的文件(/home/some_dir/foo.txt)。 您可能还想查看tempfile.TemporaryFile和NamedTemporaryFile,这将执行上述操作,并在关闭时自动从磁盘中删除。

是的,对于可读但唯一的文件名,这是一个很好的策略。

一个重要的变化:您应该用os.path.lexists替换os.path.isfile!正如现在编写的,如果有一个名为/foo/bar.baz的目录,您的程序将尝试用新文件覆盖它(这将不起作用)。。。因为isfile只检查文件而不检查目录。lexists检查目录、符号链接等。。。基本上,如果有任何原因,文件名无法创建。

编辑:@Brian给出了一个更好的答案,从比赛条件来看,这个答案更安全、更健壮。

相关问题 更多 >