python 文件原子写入

1. 文件原子写入

你的代码以写的方式打开了一个文件,执行写入操作,但在写入过程中,发生了异常,导致程序崩溃,这个时候,文件里已经有了一部分你写入的数据,但是不完整,这往往是一个不被接受的结果。文件原子写入,是指一次写入操作,要么是全部数据都写入文件,要么是全部数据都没有写入文件。

下面通过一个实验,来解释文件原子写入的必要性

with open('userlist1', 'w')as f:
    f.write('xiaohong\n')
    f.flush()
    0 / 0
    f.write('xiaogang\n')

代码创建一个userlist1文件,希望能写入两个学生的姓名,但是恰好发生了异常,这时,文件已经被创建,而且xiaohong已经被写入到文件中,如果程序在其他地方捕获了这个异常却没有提醒你,那么你可能以为这次写入操作是正常的,或者仅仅从结果上看,文件确实创建了,而且里面有数据,但数据不是全部,有一部分缺失。

2. 借助临时文件

为了实现文件原子写入,一个简单可行的思路是借助临时文件,先将数据写入到临时文件,临时文件写入成功以后再将文件重命名,这样,就不存在写入部分数据的情况了。

import os
f = open('userlist1_tmp', 'w')
f.write('xiaohong\n')
# 0/0
f.write('xiaogang\n')
f.flush()       # 确保数据写入磁盘
os.fsync(f.fileno())
f.close()

os.rename('userlist1_tmp', 'userlist1')

注意这段代码并不会保证写文件不发生异常,如果将0/0 这行代码的注释去掉,异常同样会发生,但这时,只有userlist1_tmp被创建,最终的目标文件userlist1 是不会被创建的,通过这种方式,就确保文件写入是原子的,不会出现部分数据被写入,部分数据未被写入的情况。

3. python-atomicwrites

上面的代码只是一种简单的实现,有许多特殊情况是没有被考虑的,比如临时文件和目标文件不在同一个文件系统上,那么也不是原子操作,同时这段代码也没有考虑跨平台的问题,这里推荐一个开源库python-atomicwrites, 使用pip install atomicwrites 进行安装。作者已经帮我们尽可能的处理了这些特殊情况,因此可以放心使用,我使用这个开源库来实现文件的原子写入

from atomicwrites import atomic_write

with atomic_write('userlist1', overwrite=True) as f:
    f.write('xiaohong\n')
    # 0/0
    f.write('xiaogang\n')

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案