如何安全地写入文件?

Ric*_*erg 15 python windows file

想象一下,您有一个用于处理某种XML文件或配置文件的库.该库将整个文件读入内存并提供编辑内容的方法.完成操作内容后,可以调用a write将内容保存回文件.问题是如何以安全的方式做到这一点.

覆盖现有文件(开始写入原始文件)显然不安全.如果write方法在完成之前失败,则最终会得到一个半写文件并且您丢失了数据.

更好的选择是在某处写入临时文件,当write方法完成后,临时文件复制到原始文件.

现在,如果副本以某种方式失败,您仍然可以在临时文件中正确保存数据.如果复制成功,您可以删除临时文件.

在POSIX系统上,我猜你可以使用rename系统调用,这是一个原子操作.但是你如何在Windows系统上做到最好?特别是,你如何使用Python处理这个问题?

另外,还有另一种安全写入文件的方案吗?

Sha*_*mar 14

如果你看到Python的文档,它清楚地提到os.rename()是一个原子操作.因此,在您的情况下,将数据写入临时文件然后将其重命名为原始文件将是非常安全的.

另一种方式可以像这样工作:

  • 让原始文件为abc.xml
  • 创建abc.xml.tmp并向其写入新数据
  • 将abc.xml重命名为abc.xml.bak
  • 将abc.xml.tmp重命名为abc.xml
  • 在正确放置新的abc.xml之后,删除abc.xml.bak

正如您所看到的那样,如果存在与tmp文件相关的任何问题并将其复制回来,您可以使用abc.xml.bak进行恢复.

  • Python无法强制重命名为原子的保证.据我所知,它只是调用操作系统的系统调用.但是,你提供的程序效果很好. (3认同)

u0b*_*6ae 11

如果你想要POSIX正确并保存,你必须:

  1. 写入临时文件
  2. 刷新和fsync文件(或fdatasync)
  3. 重命名原始文件

请注意,调用fsync对性能有不可预测的影响 - 因此,取决于其他未完成的I/O,ext3上的Linux可能会停止磁盘I/O整数秒.

请注意,rename不是在POSIX一个原子操作-至少相对于按照您的预期文件数据.但是,大多数操作系统和文件系统都将以这种方式工作.但似乎你错过了关于Ext4和文件系统保证原子性的非常大的linux讨论.我不知道确切的链接位置,但这是一个开始:ext4和数据丢失.

但请注意,在许多系统上,重命名在实践中与您期望的一样安全.然而,在所有可能的Linux配置中,它不可能同时获得性能和可靠性!

通过写入临时文件,然后重命名临时文件,可以预期操作是依赖的并且将按顺序执行.

然而,问题是大多数(如果不是全部)文件系统将元数据和数据分开.重命名只是元数据.这对您来说可能听起来很糟糕,但是文件系统会重新定义数据元素(例如,在HFS +或Ext3,4中使用日记)!原因是元数据较轻,如果元数据损坏,整个文件系统都会损坏 - 文件系统当然必须自行保存,然后按顺序保留用户的数据.

Ext4 rename在它刚出现时确实打破了期望,但是增加了启发式来解决它.问题不是重命名失败,而是成功重命名.Ext4可能已成功注册重命名,但如果此后不久发生崩溃,则无法写出文件数据.结果是一个0长度的文件,既不是orignal也不是新数据.

简而言之,POSIX没有这样的保证.阅读链接的Ext4文章了解更多信息!


Mic*_*der 5

在Win API中,我发现了一个相当不错的功能ReplaceFile,即使使用可选的备份,它的名称也可以实现。DeleteFileMoveFile组合总是有办法。

总的来说,您想做的事真的很好。我想不出更好的写方案。

  • 如果使用调用MS库API的正确Python代码进行说明,那就更好了。 (3认同)

mik*_*iku 5

一个简单的解决方案。使用tempfile创建一个临时文件,如果写入成功的只是文件重命名为原来的配置文件。

请注意,rename不是原子跨文件系统。为了真正安全,您将不得不采取轻微的解决方法(例如,目标文件系统上的临时文件,然后重命名)。

要锁定文件,请参阅portallocker

  • 如果 tempfile 是在目标文件系统之外的另一个文件系统中创建的,那么最终的重命名要么不起作用,要么不是原子性的。 (3认同)