读取整个文件是不是会使文件句柄保持打开状态?

Posted

技术标签:

【中文标题】读取整个文件是不是会使文件句柄保持打开状态?【英文标题】:Does reading an entire file leave the file handle open?读取整个文件是否会使文件句柄保持打开状态? 【发布时间】:2011-11-16 14:38:06 【问题描述】:

如果您使用content = open('Path/to/file', 'r').read() 读取整个文件,文件句柄是否保持打开状态直到脚本退出?有没有更简洁的方法来读取整个文件?

【问题讨论】:

【参考方案1】:

这个问题的答案在某种程度上取决于特定的 Python 实现。

要了解这一切,请特别注意实际的file 对象。在您的代码中,该对象仅在表达式中被提及一次,并且在 read() 调用返回后立即变得不可访问。

这意味着文件对象是垃圾。剩下的唯一问题是“垃圾收集器何时会收集文件对象?”。

在使用引用计数器的 CPython 中,这种垃圾会立即被注意到,因此会立即被收集。其他 python 实现通常不是这样。

更好的解决方案是确保文件已关闭,是这种模式:

with open('Path/to/file', 'r') as content_file:
    content = content_file.read()

在块结束后总是会立即关闭文件;即使发生异常。

编辑:更详细地说:

除了在with 上下文管理器设置中“自动”调用的file.__exit__() 之外,自动调用file.close() 的唯一其他方式(即,除了自己显式调用之外)是通过@ 987654330@。这就引出了一个问题,__del__() 什么时候被调用?

一个正确编写的程序不能假定终结器会在程序终止之前的任何时候运行。

-- https://devblogs.microsoft.com/oldnewthing/20100809-00/?p=13203

特别是:

对象永远不会被显式销毁;但是,当它们变得无法访问时,它们可能会被垃圾收集。 允许实现推迟垃圾收集或完全忽略它——只要没有收集到仍然可访问的对象,如何实现垃圾收集取决于实现质量。

[...]

CPython 目前使用引用计数方案,对循环链接的垃圾进行(可选)延迟检测,该方案会在大多数对象变得不可访问时立即收集它们,但不能保证收集包含循环引用的垃圾。

-- https://docs.python.org/3.5/reference/datamodel.html#objects-values-and-types

(强调我的)

但正如它所暗示的,其他实现可能有其他行为。例如,PyPy has 6 different garbage collection implementations!

【讨论】:

有一段时间,没有真正的其他 Python 实现;但依赖实现细节并不是真正的 Pythonic。 它仍然是特定于实现的,还是已经标准化了?在这种情况下不调用__exit__() 听起来像是设计缺陷。 @jgmjgm 正是因为这三个问题,GC 是不可预测的,try/finally 是繁琐的,而with 解决的清理处理程序非常常见的无用处。 “显式关闭”和“使用with 管理”之间的区别在于即使抛出异常也会调用退出处理程序。您可以将 close() 放在 finally 子句中,但这与使用 with 并没有太大区别,有点混乱(多出 3 行而不是 1 行),而且更难做到恰到好处。跨度> 我不明白这就是为什么 'with' 将不再可靠,因为它也不是明确的。是因为规范说它必须这样做,它总是这样实现吗? @jgmjgm 它更可靠,因为with foo() as f: [...]f = foo()f.__enter__()、[...] 和f.__exit__() 基本相同处理异常,这样__exit__ 总是被调用。所以文件总是被关闭。【参考方案2】:

您可以使用pathlib。

对于 Python 3.5 及更高版本:

from pathlib import Path
contents = Path(file_path).read_text()

对于旧版本的 Python,请使用 pathlib2:

$ pip install pathlib2

然后:

from pathlib2 import Path
contents = Path(file_path).read_text()

这是实际的read_textimplementation:

def read_text(self, encoding=None, errors=None):
    """
    Open the file in text mode, read it, and close the file.
    """
    with self.open(mode='r', encoding=encoding, errors=errors) as f:
        return f.read()

【讨论】:

我遇到了这个解决方案的问题,也许有人可以回答my question?提前致谢。【参考方案3】:

好吧,如果你必须逐行读取文件来处理每一行,你可以使用

with open('Path/to/file', 'r') as f:
    s = f.readline()
    while s:
        # do whatever you want to
        s = f.readline()

或者更好的方法:

with open('Path/to/file') as f:
    for line in f:
        # do whatever you want to

【讨论】:

【参考方案4】:

而不是将文件内容作为单个字符串检索, 将内容存储为文件包含的所有行的列表会很方便

with open('Path/to/file', 'r') as content_file:
    content_list = content_file.read().strip().split("\n")

可以看出,需要将连接方法.strip().split("\n") 添加到the main answer in this thread。

这里,.strip() 只是删除了整个文件字符串末尾的空格和换行符, 而.split("\n") 通过在每个换行符 \n处拆分整个文件字符串来生成实际列表。

此外, 这样,整个文件内容可以存储在一个变量中,这在某些情况下可能是需要的,而不是像this previous answer 中指出的那样逐行循环文件。

【讨论】:

以上是关于读取整个文件是不是会使文件句柄保持打开状态?的主要内容,如果未能解决你的问题,请参考以下文章

如果我需要每秒读取文件 0...5 次,是不是值得保持文件描述符打开?

此文件对象是不是保持打开状态?

在扩展坞保持活动状态时隐藏 NSMenu

如何替换当前打开的文件?

Linux 系统级开启文件句柄 调优

perl打开读取文件(open)