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

2025-03-05 09:15:00
admin
原创
67
摘要:问题描述:我正在使用 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()
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2757  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1693  
  在全球化的浪潮下,企业的业务范围不断拓展,跨文化协作变得愈发普遍。不同文化背景的团队成员在合作过程中,由于语言、价值观、工作习惯等方面的差异,往往会面临诸多沟通挑战。而产品生命周期管理(PLM)系统作为企业管理产品全生命周期的重要工具,如何有效支持跨文化协作成为了关键问题。通过合理运用沟通策略,PLM系统能够在跨文化团...
plm是什么软件   15  
  PLM(产品生命周期管理)系统在企业的产品研发、生产与管理过程中扮演着至关重要的角色,其中文档版本控制是确保产品数据准确性、完整性和可追溯性的关键环节。有效的文档版本控制能够避免因版本混乱导致的错误、重复工作以及沟通不畅等问题,提升企业整体的运营效率和产品质量。接下来,我们将深入探讨 PLM 系统实现文档版本控制的 6...
plm是什么意思   19  
  PLM(产品生命周期管理)项目管理旨在通过有效整合流程、数据和人员,优化产品从概念到退役的整个生命周期。在这个过程中,敏捷测试成为确保产品质量、加速交付的关键环节。敏捷测试强调快速反馈、持续改进以及与开发的紧密协作,对传统的测试流程提出了新的挑战与机遇。通过对测试流程的优化,能够更好地适应PLM项目的动态变化,提升产品...
plm管理系统   18  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用