在Python中,如何在与另一个文件相同的目录中创建临时文件?

27

我需要更新一个文件。我读入它,进行更改后写出来。然而,我更愿意把更改后的内容写入到一个临时文件中,再将其重命名为原文件。

temp = tempfile.NamedTemporaryFile()
tempname = temp.name
temp.write(new_data)
temp.close()
os.rename(tempname, data_file_name)
问题是tempfile.NamedTemporaryFile()创建的临时文件在不同的文件系统中(/tmp),这意味着os.rename()失败。如果我使用shlib.move()代替,则没有mv提供的原子更新(对于相同文件系统中的文件,等等)。 我知道tempfile.NamedTemporaryFile()接受一个dir参数,但data_file_name可能是"foo.txt",这种情况下dir='.';或者data_file_name可能是"/path/to/the/data/foo.txt",这种情况下dir="/path/to/the/data"。 我真正想要的是将临时文件命名为data_file_name + "some random data"。 这将有助于以留下有用线索的方式失败。 有什么建议吗?

3
如果你不想把临时文件放在临时文件目录中,为什么一开始要使用临时文件呢?为什么不使用普通文件呢? - David Zwicker
1
David: 我想使用临时文件,因为我希望更新是原子的(或者尽可能原子,通过 os.rename() 函数可以实现)。也就是说,如果文件系统填满或出现其他问题,我不希望文件只被写了一半。 - TomOnTime
这可能很难实现,因为你永远不知道写入到不同文件夹的文件是否在与本地目录相同的文件系统上。我认为将输出组织成某种提交的优点是显而易见的。为了相对确保这一点,我可能会管理自己的临时目录--尽管您还需要关心清理此文件夹。 - David Zwicker
5个回答

41

你可以使用以下方法:

  • prefix,使临时文件与原文件同名。
  • dir,指定临时文件存储位置。
  • os.path.split,从文件名中分离出目录路径和文件名。

import tempfile
import os
dirname, basename = os.path.split(filename)
temp = tempfile.NamedTemporaryFile(prefix=basename, dir=dirname)
print(temp.name)

如果文件名为'foo',则目录名将为''。我很惊喜地发现NamedTemporaryFile与dir=''一样适用,就像dir=None一样。谢谢! - TomOnTime
4
为了明确表达,你必须在NamedTemporaryFile构造函数中传递delete=False参数,否则文件将在关闭时被删除。 - moeffju

11

您可以在“dir”构造参数中传递文件位置。它能按照您的要求工作。

>>> t = tempfile.NamedTemporaryFile(dir="/Users/rafal")
>>> t.name
'/Users/rafal/tmplo45Js'

来源: http://docs.python.org/library/tempfile.html#tempfile.NamedTemporaryFile

这里介绍了Python中的tempfile模块,该模块提供了用于生成临时文件和目录的函数。NamedTemporaryFile是其中一个函数,它可用于创建一个具有唯一名称的临时文件,使用完后将自动删除。可以通过指定delete=False参数来避免自动删除。在使用时,应注意相关的文件句柄或描述符是否已关闭或释放。

2
信息:如果没有使用delete=False,文件处理器关闭后文件将被删除。 - gecco
这假设我们知道dir是什么。 - TomOnTime
我们知道这点。OP的问题已经包含将路径拆分成目录和文件名,因此在这里再写一遍是多余的。 - Rafał Rawicki

4
为了满足您的所有清单,我认为您需要使用...
temp = tempfile.NamedTemporaryFile(prefix=data_file_name, dir=path,
                                   delete=False)

delete=False 很重要,否则:

[...] 如果设置为 true(默认值),文件关闭后就会被删除。


这假设我们知道路径是什么。 - TomOnTime

1

我使用当前时间作为“一些随机数据”,附加到基本字符串上,以创建一个唯一的临时文件名:

import time

temp_file_name = data_file_name + str(time.time()) 

这很诱人,但我已经看到足够多的安全问题,因为人们自己滚动临时文件系统,所以我知道要使用tempfile提供的那个。 - TomOnTime
1
在这里补充一下,几年后如果在同一毫秒内运行两次,这将会产生冲突,在大多数人的使用情况下这是很可能发生的。最好使用官方实现的tempfile等内容,以避免竞争条件。 - daboross
如果您使用许多并行实例运行代码(否则为什么需要唯一的文件名?),冲突几乎是肯定的...在一个分布式大规模并行系统中,我最终使用了主机名、进程ID和时间的组合... - Roux

-1

您使用的tempfile模块提供了一种安全的管理临时文件的方式。如果您真的想使用自己的系统,您应该意识到它可能容易受到攻击(特别是符号链接攻击)。

生成一个临时唯一文件名的简单方法(尽管名称相对较长)是:

import uuid
import os

tempfilename = 'myprefix-%s.dat' % str(uuid.uuid4())

with open(tempfilename, 'rw') as tempfile:
    # do stuff

os.remove(tempfilename)

但这有点hackish; 实际上,最好考虑使用正确的前缀和dir参数传递给NamedTemporaryFile的tempfile模块,如其他答案中所述。

这看起来很诱人,但我见过足够多的安全问题是由于人们自己编写临时文件系统所造成的,因此我知道要使用tempfile提供的那一个。 - TomOnTime
绝对的,在生产代码中使用这样的东西是一个坏主意。在不需要考虑这个问题的环境下(例如模拟数据的日志记录),uuid 提供了一种生成唯一随机字符串的方法。 - G-J

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接