迭代文件内容
一种常见的文件操作是迭代其内容,并在迭代过程中反复采取某种措施。
在这里的所有示例中,我都将使用一个名为process的虚构函数来表示对每个字符或行所做的处理,你可以用自己的喜欢的方式实现这个函数。下面是一个简单的示例:
def process(string):
print('Processing:', string)
更有用的实现包括将数据存储在数据结构中、计算总和、使用模块re进行模式替换以及添加行号。
另外,要尝试运行这些示例,应将变量filename设置为实际使用的文件的名称。
- 每次一个字符(或字节)
一种最简单(也可能是最不常见)的文件内容迭代方式是,在while循环中使用方法read。例如,你可能想遍历文件中的每个字符(在二进制模式下是每个字节),为此可像以下代码所示的那样做。如果你每次读取多个字符(字节),可指定要读取的字符(字节)数。
- 使用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)出现了两次,而代码重复通常被视为坏事。(还记得懒惰是一种美德吗?)为避免这种重复,可使用while True/break技巧。修改后的代码如以下所示。
以不同的方式编写循环
with open(filename) as f:
while True:
char = f.read(1)
if not char: break
process(char)
前面说过,不应过多地使用break语句,因为这会导致代码更难理解。尽管如此,这段代码通常胜过前段代码,正是因为它避免了重复的代码。
- 每次一行
处理文本文件时,你通常想做的是迭代其中的行,而不是每个字符。通过使用节介绍的方法readline,可像迭代字符一样轻松地迭代行,如代码清单11-8所示。
在while循环中使用readline
with open(filename) as f:
while True:
line = f.readline()
if not line: break
process(line)
- 读取所有内容
如果文件不太大,可一次读取整个文件;为此,可使用方法read并不提供任何参数(将整个文件读取到一个字符串中),也可使用方法readlines(将文件读取到一个字符串列表中,其中每个字符串都是一行)。以下代码表明,通过这样的方式读取文件,可轻松地迭代字符和行。请注意,除进行迭代外,像这样将文件内容读取到字符串或列表中也对完成其他任务很有帮助。例如,可对字符串应用正则表达式,还可将列表存储到某种数据结构中供以后使用。
使用read迭代字符
with open(filename) as f:
for char in f.read():
process(char)
使用readlines迭代行
with open(filename) as f:
for line in f.readlines():
process(line)
- 使用 fileinput 实现延迟行迭代
有时候需要迭代大型文件中的行,此时使用readlines将占用太多内存。当然,你可转而结合使用while循环和readline,但在Python中,在可能的情况下,应首选for循环,而这里就属于这种情况。你可使用一种名为延迟行迭代的方法——说它延迟是因为它只读取实际需要的文本部分。
模块fileinput会负责打开文件,你只需给它提供一个文件名即可。
import fileinput
for line in fileinput.input(filename):
process(line)
- 文件迭代器
文件实际上是可迭代的,这意味着可在for循环中直接使用它们来迭代行,如以下代码所示。
with open(filename) as f:
for line in f:
process(line)
在这些迭代示例中,我都将文件用作了上下文管理器,以确保文件得以关闭。虽然这通常是个不错的主意,但只要不写入文件,就并非一定要这样做。如果你愿意让Python去负责关闭文件,可进一步简化这个示例,如以下代码所示。在这里,我没有将打开的文件赋给变量(如其他示例中使用的变量f),因此没法显式地关闭它。
在不将文件对象赋给变量的情况下迭代文件
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 line\n', 'Second line\n', 'Third and final line\n']
>>> first, second, third = open('somefile.txt')
>>> first
'First line\n'
>>> second
'Second line\n'
>>> third
'Third and final line\n'
在这个示例中,需要注意如下几点。
- 使用了print来写入文件,这将自动在提供的字符串后面添加换行符。
- 对打开的文件进行序列解包,从而将每行存储到不同的变量中。(这种做法不常见,因为通常不知道文件包含多少行,但这演示了文件对象是可迭代的。)
- 写入文件后将其关闭,以确保数据得以写入磁盘。(如你所见,读取文件后并没有将其关闭。这可能有点粗糙,但并非致命的。)