python 脚本中断时读取和写入文件会添加意外的字符

python read and write to file adds unexpected chars when script interrupted

这是我的问题,我有一个有很多步骤的脚本,基本上它打开一个文件,读取它,然后在读取后写回文件。脚本完成后一切都很好。当出现某种异常或脚本被中断时,就会出现问题。 我以 ‘r+’ 模式打开文件,因为如果我以 ‘w’ 模式打开它,文件会立即变为空白,如果脚本被中断,它会保持空白,而我希望它保持以前的值。 下面是一个示例,但不是我的确切脚本 运行 如果脚本被中断(或发生异常,即使被处理),test.txt 中的值将是“myVar=13e”或“myVar=13ne”。不总是,但经常。 为什么会发生以及如何避免?

import time
from test import myVar
file_path = "./test.py"
with open(file_path, 'r+', encoding=‘utf-8’) as f:
    # read the file content which is for example “myVar=11”
    # do calculations with myVar
    #str_to_oc = "myVar="+str(row[0]) #row[0] is fetched from database, it’s ID of the record. It’s an integer
    str_to_oc = “myVar=“+str(13) # I hardcoded the 13 value here instead of the database row[0]
    time.sleep(3) #just adding a delay so you can interrupt easily
    # write back a string “myVar=13” which is he new value of 13
    f.write(str_to_oc)

编辑了代码示例以便于测试

还有一点:由于脚本所在的系统的默认编码 运行,这样的事情可能会发生。解决方案是始终在读取和写入时使用 encoding='utf_8'

之类的内容明确指定编码

一个非常天真的解决方案就是将文件读入内存,假设它像您的示例所暗示的那样短,并在发生异常时重写文件内容。您还可以使用一个临时文件来避免破坏原始文件,然后只在成功时写入。

您看到了缓冲效果。

您可以通过添加 flush 调用来降低影响:

    f.write(str_to_oc)
    f.flush()

A CTRL/C 异步到达,因此这不会完全修复它。 另外,如果您选择插入/删除, 以便个别记录和整体文件大小发生变化, 你会对旧记录和新记录的错位感到不满。

在幕后,偶尔 io.BufferedWriter 请求原始 write, 变成 OS 级 syscall。 您说 CTRL/C 或致命堆栈跟踪导致程序提前终止。 在那种情况下,整个 python 解释器进程退出, 导致隐式 close(), 这可能导致从文件中读取旧字节和新字节的组合。 请注意,多字节 UTF8 代码点可以跨越磁盘块, 这可能会导致不快乐。

鉴于观察到的程序可靠性, 听起来你最好不要改动原件 直到处理成功完成:

tmp_path = file_path + '.tmp'
with open(file_path) as fin:
    with open(tmp_path, 'w') as fout:
        for line in fin:
            # (do stuff, compute output)
            fout.write(out_line + '\n')

os.rename(tmp_path, file_path)  # atomic operation, all-or-nothing

为了任何感兴趣的人,我做了一些 hack-ish 的事情,并将注释附加到我写入文件的字符串中..

str_to_oc = “myVar=“+str(13)+”#”