仍在"潜入"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
我真的做需要的文件是在一个单一的目录和编号重复是在我的情况肯定是可以接受的,所以我不是在寻找一个更可靠的方法(寿"我想任何指针欢迎),但只是为了确保这样做是以正确的方式完成的.
一个问题是上面的代码中存在竞争条件,因为测试存在和创建文件之间存在差距.这可能会对安全性产生影响(想想有人恶意地将符号链接插入到他们无法覆盖的敏感文件中,但是你的程序运行时具有更高的权限)这样的攻击就像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.isfile
同os.path.lexists
!正如现在所写,如果有一个名为/foo/bar.baz的目录,你的程序将尝试使用新文件覆盖它(这将无效)...因为isfile
只检查文件而不是目录. lexists
检查目录,符号链接等...基本上如果有任何原因无法创建文件名.
编辑:@Brian提供了一个更好的答案,在竞争条件方面更安全和更强大.