打开已经打开的文件不会引发异常

6

Consider those two python programs:

script_a.py:

from datetime import datetime
from time import sleep

while True:
    sleep(1)
    with open('foo.txt', 'w') as f:
        sleep(3)
        s = str(datetime.now())
        f.write(s)
        sleep(3)

script_b.py:

while True:
    with open('foo.txt') as f:
        s = f.read()
        print s

运行 script_a.py。当它在运行时,请启动script_b.py。它们两个都可以愉快地运行,但是如果文件当前由script_a.py打开,则script_b.py将输出一个空字符串。

我原本以为会引发一个IOError异常,告诉我文件已经被打开了,但这并没有发生,相反文件看起来是空的。为什么会这样,如何正确地检查它是否被另一个进程打开?是否可以简单地检查返回的空字符串并重试,直到读取到其他内容,还是有更具Python风格的方法?


5
只有在Windows写入文件时才会锁定文件。而POSIX平台则不会,这不是Python的问题,而是您的操作系统的特性。 - Martijn Pieters
你必须使用锁;明确地通过一个进程将文件锁定为写入模式。 - Martijn Pieters
哦,我不知道这与操作系统有关,谢谢!如果是这样,我想知道这个问题是否仍然适合在stackoverflow上提问... - soerface
2个回答

3

只要操作系统不阻止,您可以随意打开文件,这在需要对文件进行复杂操作时非常有用。

script_b.py认为文件为空的原因是文件确实为空:

with open('foo.txt', 'w') as f:

打开文件时若以w模式,会立即清空(即截断)文件。在script_a有一个三秒的初始间隔期,此时文件完全为空白,这就是script_b所看到的。在你调用f.write之后的下一个三秒间隔期内,文件仍然可能为空。这是由于缓存机制导致的——在你使用write写入的内容不能保证都被写入磁盘文件中,直到你执行close操作(即退出上下文管理器块)或手动调用flush方法使得缓存内容被写入磁盘。另外,你也可以打开非缓冲模式,这样写入的数据总是会立即写入磁盘。
with open('foo.txt','w',0) as f:
   #no buffering, f.write() writes immediately to disk

两个脚本都打开文件进行读取,每个open()截断文件。然后还有缓冲区;写入对于文件的其他读者不是立即可见的,直到缓冲区被刷新。 - Martijn Pieters
好的,script_bopen不会截断,因为它处于r模式。你说得对,在script_a中的第二个3秒窗口中,我没有处理缓冲。 - roippi

3
请参考其他答案和评论了解Python中如何处理多文件打开。如果您已经阅读了所有内容,并且仍然想在POSIX平台上锁定文件访问,则可以使用fcntl库。请注意:A)其他程序可能会忽略您对文件的锁定,B)一些网络文件系统不太好地实现了锁定,或者根本没有实现C)请务必非常小心地释放锁并避免死锁,因为flock无法检测到它[1][2]

Example.... script_a.py

from datetime import datetime
from time import sleep
import fcntl

while True:
    sleep(1)
    with open('foo.txt', 'w') as f:
        s = str(datetime.now())

        print datetime.now(), "Waiting for lock"
        fcntl.flock(f, fcntl.LOCK_EX)
        print datetime.now(), "Lock clear, writing"

        sleep(3)
        f.write(s)

        print datetime.now(), "releasing lock"
        fcntl.flock(f, fcntl.LOCK_UN)

script_b.py

import fcntl
from datetime import datetime

while True:
    with open('foo.txt') as f:
        print datetime.now(), "Getting lock"
        fcntl.flock(f, fcntl.LOCK_EX)
        print datetime.now(), "Got lock, reading file"

        s = f.read()

        print datetime.now(), "Read file, releasing lock"
        fcntl.flock(f, fcntl.LOCK_UN)

        print s

希望这有所帮助!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接