这是获取Python中文件名唯一版本的最佳方法吗?
我还在学习Python,想确保自己没有漏掉什么。我写了一个脚本,可以从多个压缩文件中提取文件,并把提取出来的文件放在一个文件夹里。为了防止文件名重复被覆盖,我写了一个小函数——我只是想知道有没有更好的方法来做到这一点?谢谢!
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
我确实需要把文件放在一个文件夹里,而且给重复的文件编号在我这儿是可以接受的,所以我并不想要更复杂的方法(不过如果有建议我也欢迎),只是想确认一下我现在的做法是否正确。
6 个回答
做了两个小改动……
base_name, ext = os.path.splitext(file_name)
你得到了两个结果,它们的意思不同,所以给它们起不同的名字。
file_name = "%s_%d%s" % (base_name, str(counter), ext)
这样做并没有更快或者明显更短。不过,当你想要更改文件名的格式时,格式只在一个地方,这样处理起来稍微简单一些。
是的,这确实是一个让文件名既易读又独特的好方法。
有一个重要的改动:你应该把 os.path.isfile
替换成 os.path.lexists
!现在的写法是,如果有一个叫做 /foo/bar.baz 的文件夹,你的程序会试图用新文件覆盖它(这会失败)……因为 isfile
只检查文件,而不检查文件夹。lexists
会检查文件夹、符号链接等……基本上就是检查一下这个文件名是否有可能无法创建。
编辑:@Brian 提供了一个更好的答案,在处理竞争条件方面更安全、更稳健。
你上面代码中的一个问题是存在竞争条件,因为在检查文件是否存在和创建文件之间有个时间差。这可能会带来安全隐患(想象一下,有人恶意地插入一个指向敏感文件的符号链接,而他们自己无法覆盖这个文件,但你的程序因为权限更高可以这样做)。像这样的攻击就是为什么像os.tempnam()这样的函数被弃用的原因。
为了避免这个问题,最好的办法是尝试以一种方式创建文件,如果失败就会抛出异常,而成功时则返回实际打开的文件对象。你可以使用更底层的os.open函数,通过同时传入os.O_CREAT和os.O_EXCL标志来实现。一旦文件打开,就返回你创建的实际文件(可选地返回文件名)。例如,下面是修改后的代码,使用这种方法(返回一个(file, filename)的元组):
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,它们会做到以上这些,并且在关闭时会自动从磁盘中删除。