在python中重写现有文件时如何处理错误

2024-06-27 09:34:51 发布

您现在位置:Python中文网/ 问答频道 /正文

当我尝试写入一个已经存在的文件,但写入失败时,该文件将被覆盖而不被任何内容覆盖(即,它将被清除-现有文件的内容将被删除)

例如:

alaa@vernal:~/Test$ echo "sometext" > testfile
alaa@vernal:~/Test$ cat testfile 
sometext

在Python中:

with open('testfile', 'w') as f:
    f.write(doesntexist)

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: name 'doesntexist' is not defined

文件为空:

alaa@vernal:~/Test$ cat testfile 
alaa@vernal:~/Test$

我试图像这样处理异常,但仍然会清空文件:

import traceback

with open('testfile', 'w') as f:
    try:
        f.write(doesntexist)
        print('OK')
    except:
        print('NOT OK')
        traceback.print_exc()

NOT OK
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
NameError: name 'doesntexist' is not defined

我尝试以不同的方式处理它,但它仍然会清空文件:

import traceback

try:
    with open('testfile', 'w') as f:
        f.write(doesntexist)
        print('OK')
except:
    print('NOT OK')
    traceback.print_exc()

NOT OK
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
NameError: name 'doesntexist' is not defined

我觉得我错过了什么。在Python中,有没有一种直接的方法来处理这种情况,即在写入由于任何原因失败时而不是清除文件?或者我必须使用“更聪明”的代码,例如:首先读取文件的内容,如果写入失败,则使用原始内容重新写入,或者可能尝试先将数据写入临时文件,等等


Tags: 文件test内容aswithnotokopen
2条回答

您正在处理的异常已经在文件被打开进行写入和截断后发生

open('testfile', 'w')

打开文件进行写入并将其截断。见docs

mode is an optional string that specifies the mode in which the file is opened... 'w' for writing (truncating the file if it already exists)...

然后:

f.write(doesntexist)

引发^{},因为您试图访问一个确实不存在的变量doesntexist。此时(处理此异常),您无法做任何事情来更改open的行为

至于做什么,这取决于具体的用例,还有其他选择

对于上面的简单示例(如果存在str变量,只需使用该变量的内容覆盖该文件),您可以这样做,例如:

from pathlib import Path
try:
    Path("testfile").write_text(doesntexist)
    print("OK")
except NameError:
    print("Not OK")

这样,在调用方法时以及在打开文件并尝试写入之前引发异常

您可以open使用a模式附加文件,但是写入操作当然是附加的

您可以准备要写入的内容,确保您拥有所有位,并且只有在您知道这样做是安全的情况下才能打开并写入


换句话说,失败的不是write。。。而且write因此open成功后失败的可能性相对较小(例如ENOSPC设备上的空间可能会用完-您会看到OSError

您看到的具体故障是由于访问不存在的变量造成的,因此与write代码之前发生的情况无关,您还可以:

stuff_to_write = doesntexist
with open('testfile', 'w') as f:
    f.write(stuff_to_write)

它会在你open之前击中NameError并截断

或:

try:
    stuff_to_write = doesntexist
    with open('testfile', 'w') as f:
        f.write(stuff_to_write)
except Exception:
    print('Oops')

尽管这有点傻

实际上,变量不存在的具体情况可以通过简单的省略发生(并且任何Link或Chor或只是运行脚本应该发现)或变量只被分配到条件块内,我将考虑正确的位置来解决这个问题。p>

open(..., 'w')立即截断文件,甚至在您有机会看到后续写入是否成功之前

假设您不需要修改现有文件,那么创建一个新的文件将更加简单和安全,一旦写入成功,您将重命名该文件。您可以将其包装在上下文管理器中,以简化业务逻辑

from tempfile import NamedTemporaryFile
from contextlib import contextmanager


def safe_write(fname, dir=None):
       
    if dir is None:
        dir = os.path.dirname(fname)
    try:
        try:
            f = None
            with NamedTemporaryFile('w', 
                                    dir=dir,
                                    delete=False) as f:
                yield f
        else:
            os.rename(f.name, fname)
    finally:
        try:
            if f is not None:
                os.remove(f.name)
        except FileNotFoundError:
            pass  # It doesn't exist if we successfully renamed it.


with safe_write('testfile') as f:
    f.write(doesntexist)

我们在与原始文件相同的目录中创建临时文件,以确保它们位于相同的文件系统中(但允许显式目录) 如果使用目标目录不合适,则命名)delete=False确保完成后仍有一个文件要重命名 用这个文件

这段代码试图对原始文件执行的唯一操作是将其原子地替换为临时文件:重命名要么成功,要么使原始文件保持完整。原子重命名仅适用于 不过,在文件系统中也是可能的。将文件移动到另一个文件系统需要实际复制文件中的数据,而不仅仅是更新其文件系统条目


在适当的位置安全地编辑一个文件是比较棘手的,我不打算在这里讨论这个问题

相关问题 更多 >