打开已打开的文件不抛出异常

6 投票
2 回答
8329 浏览
提问于 2025-04-17 23:42

考虑这两个 Python 程序:

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 方法呢?

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
3

请查看其他答案和评论,了解在Python中如何处理多个文件打开的情况。如果你已经看过这些内容,仍然想在POSIX平台上锁定文件访问,那么你可以使用fcntl库。

要记住以下几点:A) 其他程序可能会忽略你对文件的锁定,B) 一些网络文件系统的锁定功能可能不好,甚至根本没有实现,C) 一定要小心释放锁,避免死锁,因为flock不会检测到这一点[1][2]

示例....

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

希望这对你有帮助!

撰写回答