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

2025-03-05 09:15:00
admin
原创
2
摘要:问题描述:我正在使用 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()
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1324  
  IPD研发管理体系作为一种先进的研发管理理念和方法,对于打造优质产品体验起着至关重要的作用。它涵盖了从产品规划、研发、上市到生命周期管理的全流程,通过整合资源、优化流程、加强团队协作等方式,确保产品能够精准满足用户需求,提升用户满意度和忠诚度。IPD研发管理体系的核心原则IPD研发管理体系以市场驱动为核心原则。这意味着...
IPD集成产品开发   8  
  IPD(Integrated Product Development)产品开发流程作为一种先进的产品开发管理模式,在众多企业中得到广泛应用。它强调跨部门团队协作、并行工程以及基于市场的产品开发理念,旨在提高产品开发效率、缩短产品上市时间、提升产品质量。而成本控制在产品开发过程中至关重要,关乎企业的利润空间和市场竞争力。...
华为IPD流程   6  
  IPD(Integrated Product Development)产品开发流程作为一种先进的产品开发管理模式,在众多企业中得到了广泛应用。它从多个维度对产品开发过程进行优化和整合,为企业创新提供了强大的支撑。通过实施IPD产品开发流程,企业能够更加高效地将创意转化为具有市场竞争力的产品,从而在激烈的市场竞争中占据优...
华为IPD流程管理   10  
  华为作为全球知名的科技企业,其产品质量在市场上有口皆碑。华为IPD产品开发流程在确保产品质量方面发挥了至关重要的作用。IPD(Integrated Product Development)即集成产品开发,是一套先进的、成熟的产品开发管理思想、模式和方法。它打破了传统产品开发中各部门之间的壁垒,强调跨部门团队协作,从产品...
IPD集成产品开发流程   9  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用