>>> from env_helper import info; info()
页面更新时间: 2023-09-05 18:46:13
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-11-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

2.4. 迭代文件内容

至此,你见识了文件对象提供的一些方法,还学习了如何获得文件对象。一种常见的文件操 作是迭代其内容,并在迭代过程中反复采取某种措施。这样做的方法有很多,你完全可以找到自己喜欢的方法并坚持使用。然而,由于其他人可能使用不同的方法,为了能够理解他们编写的程序,你应熟悉所有的基本方法。

在本节的所有示例中,我都将使用一个名为process的虚构函数来表示对每个字符或行所做 的处理,你可以用自己的喜欢的方式实现这个函数。下面是一个简单的示例:

>>> def process(string):
>>>     print('Processing:', string)

更有用的实现包括将数据存储在数据结构中、计算总和、使用模块re进行模式替换以及添加行号。

另外,要尝试运行这些示例,应将变量filename设置为实际使用的文件的名称。

2.4.1. 每次一个字符(或字节)

一种最简单(也可能是最不常见)的文件内容迭代方式是,在while循环中使用方法read。 例如,你可能想遍历文件中的每个字符(在二进制模式下是每个字节),为此可像代码清单11-6 所示的那样做。如果你每次读取多个字符(字节),可指定要读取的字符(字节)数。

代码清单11 -6 使用read遍历字符

with open(filename) as f: char = f.read(1) while char: process(char) char = f.read(1)

这个程序之所以可行,是因为到达文件末尾时,方法read将返回一个空字符串,但在此之前, 返回的字符串都只包含一个字符(对应于布尔值True)。只要char为True,你就知道还没结束。

如你所见,赋值语句char = f.read(1)出现了两次,而代码重复通常被视为坏事。(还记得懒 惰是一种美德吗?)为避免这种重复,可使用第5章介绍的while True/break技巧。修改后的代码 如代码清单11-7所示。

代码清单11 -7 以不同的方式编写循环

with open(filename) as f:
    while True:
        char = f.read(1)
        if not char: break
        process(char)

第5章说过,不应过多地使用break语句,因为这会导致代码更难理解。尽管如此,代码清单 11-7通常胜过代码清单11-6,正是因为它避免了重复的代码。

2.4.2. 每次一行

处理文本文件时,你通常想做的是迭代其中的行,而不是每个字符。通过使用之前介绍的方法readline,可像迭代字符一样轻松地迭代行,如代码清单11-8所示。

代码清单11 -8 在while循环中使用readline

with open(filename) as f:
    while True:
        line = f.readline()
        if not line: break
        process(line)

2.4.3. 读取所有内容

如果文件不太大,可一次读取整个文件;为此,可使用方法read并不提供任何参数(将整个 文件读取到一个字符串中),也可使用方法readlines(将文件读取到一个字符串列表中,其中每 个字符串都是一行)。代码清单11-9和11-10表明,通过这样的方式读取文件,可轻松地迭代字符 和行。请注意,除进行迭代外,像这样将文件内容读取到字符串或列表中也对完成其他任务很有 帮助。例如,可对字符串应用正则表达式,还可将列表存储到某种数据结构中供以后使用。

代码清单11 -9 使用read迭代字符

with open(filename) as f:
    for char in f.read():
        process(char)

代码清单11 -10 使用readlines迭代行

with open(filename) as f:
    for line in f.readlines():
        process(line)

2.4.4. 使用 fileinput 实现延迟行迭代

有时候需要迭代大型文件中的行,此时使用readlines将占用太多内存。当然,你可转而 结合使用while循环和readline,但在Python中,在可能的情况下,应首选for循环,而这里就 属于这种情况。你可使用一种名为延迟行迭代的方法——说它延迟是因为它只读取实际需要的 文本部分。

请注意,模块fileinput会负责打开文件,你只需给它提供一个文件名即可。

代码清单11 -11 使用fileinput迭代行

import fileinput
for line in fileinput.input(filename):
    process(line)

2.4.5. 文件迭代器

该来看看最酷(也是最常见)的方法了。文件实际上是可迭代的,这意味着可在for循环中 直接使用它们来迭代行,如代码清单11-12所示。

代码清单11 -12 迭代文件

with open(filename) as f:
    for line in f:
        process(line)

在这些迭代示例中,我都将文件用作了上下文管理器,以确保文件得以关闭。虽然这通常是 个不错的主意,但只要不写入文件,就并非一定要这样做。如果你愿意让Python去负责关闭文件, 可进一步简化这个示例,如代码清单11-13所示。在这里,我没有将打开的文件赋给变量(如其 他示例中使用的变量f),因此没法显式地关闭它。

代码清单11 -13 在不将文件对象赋给变量的情况下迭代文件

for line in open(filename):
    process(line)

请注意,与其他文件一样,sys.stdin也是可迭代的,因此要迭代标准输入中的所有行,可像下面这样做:

>>> import sys
>>> for line in sys.stdin:
>>>     process(line)

另外,可对迭代器做的事情基本上都可对文件做,如(使用list(open(filename)))将其转 换为字符串列表,其效果与使用readlines相同。

>>> f = open('somefile.txt', 'w')
>>> print('First', 'line', file=f)
>>> print('Second', 'line', file=f)
>>> print('Third', 'and final', 'line', file=f)
>>> f.close()
>>> lines = list(open('somefile.txt'))
>>> lines
['First linen', 'Second linen', 'Third and final linen']
>>> first, second, third = open('somefile.txt')
>>> first
'First linen'
>>> second
'Second linen'
>>> third
'Third and final linen'

在这个示例中,需要注意如下几点。

  • 使用了print来写入文件,这将自动在提供的字符串后面添加换行符。

  • 对打开的文件进行序列解包,从而将每行存储到不同的变量中。(这种做法不常见,因为 通常不知道文件包含多少行,但这演示了文件对象是可迭代的。)

  • 写入文件后将其关闭,以确保数据得以写入磁盘。(如你所见,读取文件后并没有将其关闭。这可能有点粗糙,但并非致命的。)