如何使文件创建成为一个原子操作?

2025-03-05 09:15:00
admin
原创
3
摘要:问题描述:我正在使用 Python 通过一次操作将大块文本写入文件:open(file, 'w').write(text) 如果脚本中断导致文件写入未完成,我希望没有文件,而不是部分完成的文件。可以这样做吗?解决方案 1:将数据写入临时文件,当数据成功写入后,将文件重命名为正确的目标文件,例如with ope...

问题描述:

我正在使用 Python 通过一次操作将大块文本写入文件:

open(file, 'w').write(text)

如果脚本中断导致文件写入未完成,我希望没有文件,而不是部分完成的文件。可以这样做吗?


解决方案 1:

将数据写入临时文件,当数据成功写入后,将文件重命名为正确的目标文件,例如

with open(tmpFile, 'w') as f:
    f.write(text)
    # make sure that all data is on disk
    # see http://stackoverflow.com/questions/7433057/is-rename-without-fsync-safe
    f.flush()
    os.fsync(f.fileno())    
os.replace(tmpFile, myFile)  # os.rename pre-3.3, but os.rename won't work on Windows

根据文档http://docs.python.org/library/os.html#os.replace

将文件或目录重命名srcdst。如果 dst 是非空目录,OSError则会引发 。如果dst存在且是文件,则在用户有权限的情况下会默默替换它。如果srcdst位于不同的文件系统上,则操作可能会失败。如果成功,重命名将是一个原子操作(这是 POSIX 要求)。

笔记:

  • 如果源和目标位置不在同一文件系统上,则可能不是原子操作

  • os.fsync如果在电源故障、系统崩溃等情况下性能/响应能力比数据完整性更重要,则可以跳过此步骤

解决方案 2:

一个使用 Python 实现原子写入的简单代码片段tempfile

with open_atomic('test.txt', 'w') as f:
    f.write("huzza")

甚至可以读取和写入同一个文件:

with open('test.txt', 'r') as src:
    with open_atomic('test.txt', 'w') as dst:
        for line in src:
            dst.write(line)

使用两个简单的上下文管理器

import os
import tempfile as tmp
from contextlib import contextmanager

@contextmanager
def tempfile(suffix='', dir=None):
    """ Context for temporary file.

    Will find a free temporary filename upon entering
    and will try to delete the file on leaving, even in case of an exception.

    Parameters
    ----------
    suffix : string
        optional file suffix
    dir : string
        optional directory to save temporary file in
    """

    tf = tmp.NamedTemporaryFile(delete=False, suffix=suffix, dir=dir)
    tf.file.close()
    try:
        yield tf.name
    finally:
        try:
            os.remove(tf.name)
        except OSError as e:
            if e.errno == 2:
                pass
            else:
                raise

@contextmanager
def open_atomic(filepath, *args, **kwargs):
    """ Open temporary file object that atomically moves to destination upon
    exiting.

    Allows reading and writing to and from the same filename.

    The file will not be moved to destination in case of an exception.

    Parameters
    ----------
    filepath : string
        the file path to be opened
    fsync : bool
        whether to force write the file to disk
    *args : mixed
        Any valid arguments for :code:`open`
    **kwargs : mixed
        Any valid keyword arguments for :code:`open`
    """
    fsync = kwargs.pop('fsync', False)

    with tempfile(dir=os.path.dirname(os.path.abspath(filepath))) as tmppath:
        with open(tmppath, *args, **kwargs) as file:
            try:
                yield file
            finally:
                if fsync:
                    file.flush()
                    os.fsync(file.fileno())
        os.rename(tmppath, filepath)

解决方案 3:

由于细节很容易搞砸,我建议使用一个小型库。库的优点是它能处理所有这些细节,并且由社区进行审查和改进。

untitakerpython-atomicwrites编写的其中一个库甚至对 Windows 有适当的支持:

警告(截至 2023 年):

此库目前无人维护。作者评论:

[...],我认为是时候弃用这个包了。Python 3 有 os.replace 和 os.rename,它们可能足以应付大多数用例。

原文推荐:

摘自自述文件:

from atomicwrites import atomic_write

with atomic_write('foo.txt', overwrite=True) as f:
    f.write('Hello world.')
    # "foo.txt" doesn't exist yet.

# Now it does.

通过 PIP 安装:

pip install atomicwrites

解决方案 4:

我正在使用此代码以原子方式替换/写入文件:

import os
from contextlib import contextmanager

@contextmanager
def atomic_write(filepath, binary=False, fsync=False):
    """ Writeable file object that atomically updates a file (using a temporary file).

    :param filepath: the file path to be opened
    :param binary: whether to open the file in a binary mode instead of textual
    :param fsync: whether to force write the file to disk
    """

    tmppath = filepath + '~'
    while os.path.isfile(tmppath):
        tmppath += '~'
    try:
        with open(tmppath, 'wb' if binary else 'w') as file:
            yield file
            if fsync:
                file.flush()
                os.fsync(file.fileno())
        os.rename(tmppath, filepath)
    finally:
        try:
            os.remove(tmppath)
        except (IOError, OSError):
            pass

用法:

with atomic_write('path/to/file') as f:
    f.write("allons-y!
")

它是基于这个食谱的。

解决方案 5:

完成后只需链接文件:

with tempfile.NamedTemporaryFile(mode="w") as f:
    f.write(...)
    os.link(f.name, final_filename)

如果你想变得更时尚:

@contextlib.contextmanager
def open_write_atomic(filename: str, **kwargs):
    kwargs['mode'] = 'w'
    with tempfile.NamedTemporaryFile(**kwargs) as f:
        yield f
        os.link(f.name, filename)

解决方案 6:

本页上的答案相当老了,现在有一些图书馆可以为您做到这一点。

特别是,safer这是一个旨在帮助防止程序员错误破坏文件、套接字连接或通用流的库。它非常灵活,除其他功能外,它还可以选择使用内存或临时文件,您甚至可以在发生故障时保留临时文件。

他们的例子正是你想要的:

# dangerous
with open(filename, 'w') as fp:
    json.dump(data, fp)
    # If an exception is raised, the file is empty or partly written
# safer
with safer.open(filename, 'w') as fp:
    json.dump(data, fp)
    # If an exception is raised, the file is unchanged.

它在 PyPI 中,只需使用安装即可,或者在https://github.com/rec/saferpip install --user safer获取最新版本

解决方案 7:

适用于 Windows 的循环文件夹和重命名文件的原子解决方案。经过测试,原子自动化,您可以增加概率以最大限度地降低发生相同文件名事件的风险。您可以使用 random.choice 方法随机化字母符号组合库,对于数字 str(random.random.range(50,999999999,2)。您可以根据需要改变数字范围。

import os import random

path = "C:\\Users\\ANTRAS\\Desktop\\NUOTRAUKA\\\"

def renamefiles():
    files = os.listdir(path)
    i = 1
    for file in files:
        os.rename(os.path.join(path, file), os.path.join(path, 
                  random.choice('ABCDEFGHIJKL') + str(i) + str(random.randrange(31,9999999,2)) + '.jpg'))
        i = i+1

for x in range(30):
    renamefiles()
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1325  
  IPD(Integrated Product Development)流程作为一种先进的产品开发管理模式,在众多企业中得到了广泛应用。它涵盖了从产品概念产生到产品退市的整个生命周期,通过整合跨部门团队、优化流程等方式,显著提升产品开发的效率和质量,进而为项目的成功奠定坚实基础。深入探究IPD流程的五个阶段与项目成功之间...
IPD流程分为几个阶段   4  
  华为作为全球知名的科技企业,其成功背后的管理体系备受关注。IPD(集成产品开发)流程作为华为核心的产品开发管理模式,其中的创新管理与实践更是蕴含着丰富的经验和深刻的智慧,对众多企业具有重要的借鉴意义。IPD流程的核心架构IPD流程旨在打破部门墙,实现跨部门的高效协作,将产品开发视为一个整体的流程。它涵盖了从市场需求分析...
华为IPD是什么   3  
  IPD(Integrated Product Development)研发管理体系作为一种先进的产品开发模式,在众多企业的发展历程中发挥了至关重要的作用。它不仅仅是一套流程,更是一种理念,一种能够全方位提升企业竞争力,推动企业持续发展的有效工具。深入探究IPD研发管理体系如何助力企业持续发展,对于众多渴望在市场中立足并...
IPD管理流程   3  
  IPD(Integrated Product Development)流程管理旨在通过整合产品开发流程、团队和资源,实现产品的快速、高质量交付。在这一过程中,有效降低成本是企业提升竞争力的关键。通过优化IPD流程管理中的各个环节,可以在不牺牲产品质量和性能的前提下,实现成本的显著降低,为企业创造更大的价值。优化产品规划...
IPD流程分为几个阶段   4  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用