(四)逐行读取与按块处理
1. 文件也是可迭代对象
# 每轮只取得一行;行尾换行仍保留,空白行也不是文件结束。
from pathlib import Path
path = Path("lines.txt")
path.write_text("Python\n\nNumPy\n", encoding="utf-8")
with path.open(encoding="utf-8") as stream:
for number, line in enumerate(stream, start=1):
print(number, repr(line))
print("末尾", repr(stream.readline()))输出 1 'Python\n'、2 '\n'、3 'NumPy\n'、末尾 ''。空白行有一个换行字符,文件末尾才没有新字符。strip() 还会去掉两端其他空白;若只准备移除行尾,可以写 rstrip("\r\n")。
read() 把全部剩余内容收进一个对象,readlines() 收进一个行列表;for line in stream 则逐行取得。在上一章的生成器中处理这些行时,后续统计可以一行接一行推进。
2. 二进制按块读取
# 一次取最多四个字节,不必先把完整文件读进内存。
from pathlib import Path
path = Path("chunks.bin")
path.write_bytes(b"abcdefghi")
total = 0
with path.open("rb") as stream:
while True:
chunk = stream.read(4)
if not chunk:
break
total += len(chunk)
print(chunk)
print("总字节数", total)输出 b'abcd'、b'efgh'、b'i'、总字节数 9。总文件大小为 n、块大小为 b 时,读取总量仍是 ,这段处理额外保留约 字节。逐行读取的空间还随最长一行增长,不能只用文件总行数判断每次读取占多少空间。
3. 用 batched 把文本行分组
# batched 消耗文件迭代器,每次交出最多两行;转换后立即处理这批数据。
from itertools import batched
from pathlib import Path
path = Path("batches.txt")
path.write_text("20\n35\n45\n", encoding="utf-8")
with path.open(encoding="utf-8") as stream:
for lines in batched(stream, 2):
values = [int(line) for line in lines]
print(values, sum(values))得到 [20, 35] 55 和 [45] 45。文件要在这段消费期间保持打开;先从 with 中返回一个还没读取的文件生成器,再在外面消费,文件已经关闭。把消费放在 with 内,或者让生成器函数自己管理文件的整个使用过程。