HOWTO · Python
如何在 Python 中从文件读取特定行
Python 可以用多种方式读取文件中的特定行:小文件可用 fileobject.readlines(),大文件可用 enumerate() 逐行扫描;需要反复访问同一文件中的行时,可用 linecache。linecache 会缓存文件数据,因此并非恒定内存用量的方案。
本页内容
要从 Python 文本文件中读取一行或多行,首先要区分行号是供人阅读的,还是供 Python 代码使用的。人通常从第 1 行开始计数,而列表索引从 0 开始。对于小文件,readlines() 会载入所有行,方便按索引读取。对于大文件,enumerate() 可以逐行扫描。需要反复查询时,linecache 可按行号访问内容,并会缓存文件数据,因此它并不是恒定内存用量的方案。
先确定行号的计数方式
如果有人要第 3 行,对应的列表索引是 3 - 1,也就是 2。enumerate() 默认也从索引 0 开始。相比之下,linecache.getline(path, 3) 接受从 1 开始的人类行号。明确区分这些计数方式,可以避免差一行的错误。文本行通常包含末尾的换行符;只有在显示或比较时不需要这段空白,才将它移除。
使用 readlines() 读取小文件中的指定行
readlines() 会返回一个包含所有行的列表;如果行末有换行符,列表项也会保留它。当文件能够轻松装入内存,而且你可能要访问多个位置时,这种方式很方便。下面的独立示例会创建一个临时文本文件,并打印其中的第三行:
本节中的代码以及显示的标签或输出仍为英文。
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as folder:
path = Path(folder) / "notes.txt"
path.write_text("first\nsecond\nthird\nfourth\n", encoding="utf-8")
with path.open(encoding="utf-8") as file:
lines = file.readlines()
print(lines[2].rstrip("\n"))
若要读取从 first_line 到 last_line(含首尾)的连续行号范围,可使用切片 lines[first_line - 1:last_line]。切片的结束索引不包含在结果中,因此可以直接将人类行号中的最后一行号用作结束索引。如果请求的行可能不存在,索引前应先确认它在 len(lines) 的范围内。
使用 enumerate() 逐行读取大文件中的指定行
直接迭代文件可以避免创建包含所有行的列表。enumerate(file) 会在读取每一行时提供一个从 0 开始的索引;若要显示人类使用的行号,就加 1。读取较后面的行仍需先经过前面的行,因此这种方式适合减少内存用量,而不是直接跳到远处的某一行。
本节中的代码以及显示的标签或输出仍为英文。
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as folder:
path = Path(folder) / "events.txt"
path.write_text("start\nready\nconnected\nfinished\n", encoding="utf-8")
wanted = {2, 4}
with path.open(encoding="utf-8") as file:
for line_number, line in enumerate(file, start=1):
if line_number in wanted:
print(line_number, line.rstrip("\n"))
if line_number >= max(wanted):
break
这里使用 start=1,使循环变量直接表示人类使用的行号,让筛选条件更容易理解。如果文件行数少于请求的行号,循环会自然结束,不会打印那些不存在的行。
使用 linecache 反复访问指定行
如果代码要根据文件从 1 开始的行号反复查询,可使用 linecache.getline(path, line_number)。它会以文本形式返回该行;如果行末有换行符,也会一并返回。行号不存在时,结果为空字符串。linecache 会缓存文件内容以供重用;当需要反复查询同一个文件时,这可能很方便,但缓存数据仍会占用内存。
本节中的代码以及显示的标签或输出仍为英文。
import linecache
from pathlib import Path
from tempfile import TemporaryDirectory
with TemporaryDirectory() as folder:
path = Path(folder) / "report.txt"
path.write_text("header\nalpha\nbeta\n", encoding="utf-8")
line = linecache.getline(str(path), 2)
print(line.rstrip("\n"))
linecache.clearcache()
示例中的显式 clearcache() 调用会在结束时释放缓存条目。对于会变化的文件,缓存是否最新很重要;如果程序修改了文件,并且之后的查询需要丢弃旧缓存,应先清除缓存。
处理行号范围、缺失行和内存限制
如果文件大小适中,且直接访问比将内容留在内存中更重要,可选择 readlines()。如果要逐步处理大文件,或在找到指定行后停止,可选择 enumerate();但要到达较后的目标行,仍需读取它之前的行。如果需要反复查询带编号的行,并且可以接受缓存占用的内存,可选择 linecache。这些方法都无法读取文件末尾之后的行:列表索引会引发 IndexError,迭代时不会匹配到该行,而 getline() 会返回空字符串。请根据程序的需求验证请求,或处理相应结果。
以读取模式打开文本文件,并尽量使用 with 语句,以便在代码块结束时关闭文件。如果已知文件的编码,请指定该编码。随着行数和行长度增加,将整个文件载入内存可能会占用大量内存;不存在适用于所有机器和工作负载的通用文件大小阈值。流式读取可以限制读取循环保留的行数,但收集起来的指定行仍会占用内存。选择方法时,访问模式和可用内存比固定的大小或速度说法更有参考价值。