至此,见识了文件对象提供的一些方法,还学习了如何获得文件对象。 一种常见的文件操作是迭代其内容,并在迭代过程中反复采取某种措施。 这样做的方法有很多,完全可以找到自己喜欢的方法并坚持使用。 然而,由于其他人可能使用不同的方法,为了能够理解他们编写的程序,应熟悉所有的基本方法。
在本节的所有示例中,都将使用一个名为 process
的虚构函数来表示对每个字符或行所做的处理,
可以用自己的喜欢的方式实现这个函数。下面是一个简单的示例:
def process(string):
print('Processing:', string)
更有用的实现包括将数据存储在数据结构中、计算总和、使用模块 re
进行模式替换以及添加行号。
另外,要尝试运行这些示例,应将变量 filename
设置为实际使用的文件的名称。
一种最简单(也可能是最不常见)的文件内容迭代方式是,
在 while
循环中使用方法 read
。 例如,可能想遍历文件中的每个字符(在二进制模式下是每个字节),
为此可像下面代码所示的那样做。如果每次读取多个字符(字节),
可指定要读取的字符(字节)数。
示例:使用 read
遍历字符。
filename='xx_somefile.txt'
with open(filename) as f:
char = f.read(1)
while char:
process(char)
char = f.read(1)
Processing: F Processing: i Processing: r Processing: s Processing: t Processing: Processing: l Processing: i Processing: n Processing: e Processing: Processing: S Processing: e Processing: c Processing: o Processing: n Processing: d Processing: Processing: l Processing: i Processing: n Processing: e Processing: Processing: T Processing: h Processing: i Processing: r Processing: d Processing: Processing: a Processing: n Processing: d Processing: Processing: f Processing: i Processing: n Processing: a Processing: l Processing: Processing: l Processing: i Processing: n Processing: e Processing:
这个程序之所以可行,是因为到达文件末尾时,方法 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)
Processing: F Processing: i Processing: r Processing: s Processing: t Processing: Processing: l Processing: i Processing: n Processing: e Processing: Processing: S Processing: e Processing: c Processing: o Processing: n Processing: d Processing: Processing: l Processing: i Processing: n Processing: e Processing: Processing: T Processing: h Processing: i Processing: r Processing: d Processing: Processing: a Processing: n Processing: d Processing: Processing: f Processing: i Processing: n Processing: a Processing: l Processing: Processing: l Processing: i Processing: n Processing: e Processing:
处理文本文件时,通常想做的是迭代其中的行,而不是每个字符。通过使用之前介绍的方法 readline
,
可像迭代字符一样轻松地迭代行,如下面代码所示。
示例:在 while
循环中使用 readline
。
with open(filename) as f:
while True:
line = f.readline()
if not line: break
process(line)
Processing: First line Processing: Second line Processing: Third and final line
如果文件不太大,可一次读取整个文件;为此,可使用方法 read
并不提供任何参数(将整个文件读取到一个字符串中),
也可使用方法 readlines
(将文件读取到一个字符串列表中,其中每个字符串都是一行)。
下面2种代码表明,通过这样的方式读取文件,可轻松地迭代字符和行。请注意,
除进行迭代外,像这样将文件内容读取到字符串或列表中也对完成其他任务很有帮助。
例如,可对字符串应用正则表达式,还可将列表存储到某种数据结构中供以后使用。
示例:使用 read
迭代字符。
with open(filename) as f:
for char in f.read():
process(char)
Processing: F Processing: i Processing: r Processing: s Processing: t Processing: Processing: l Processing: i Processing: n Processing: e Processing: Processing: S Processing: e Processing: c Processing: o Processing: n Processing: d Processing: Processing: l Processing: i Processing: n Processing: e Processing: Processing: T Processing: h Processing: i Processing: r Processing: d Processing: Processing: a Processing: n Processing: d Processing: Processing: f Processing: i Processing: n Processing: a Processing: l Processing: Processing: l Processing: i Processing: n Processing: e Processing:
示例:使用 readlines
迭代行。
with open(filename) as f:
for line in f.readlines():
process(line)
Processing: First line Processing: Second line Processing: Third and final line
有时候需要迭代大型文件中的行,此时使用 readlines
将占用太多内存。
当然,可转而结合使用 while
循环和 readline
,但在Python中,
在可能的情况下,应首选 for
循环,而这里就属于这种情况。
可使用一种名为延迟行迭代的方法,说它延迟是因为它只读取实际需要的文本部分。
请注意,模块 fileinput
会负责打开文件,只需给它提供一个文件名即可。
示例:使用 fileinput
迭代行。
import fileinput
for line in fileinput.input(filename):
process(line)
Processing: First line Processing: Second line Processing: Third and final line
该来看看最酷(也是最常见)的方法了。文件实际上是可迭代的,
这意味着可在 for
循环中 直接使用它们来迭代行,如下面代码所示。
示例:迭代文件。
with open(filename) as f:
for line in f:
process(line)
Processing: First line Processing: Second line Processing: Third and final line
在这些迭代示例中,都将文件用作了上下文管理器,以确保文件得以关闭。
虽然这通常是个不错的主意,但只要不写入文件,就并非一定要这样做。
如果愿意让Python去负责关闭文件, 可进一步简化这个示例,如下面代码所示。
在这里,没有将打开的文件赋给变量(如其他示例中使用的变量 f
),因此没法显式地关闭它。
示例:在不将文件对象赋给变量的情况下迭代文件。
for line in open(filename):
process(line)
Processing: First line Processing: Second line Processing: Third and final line
请注意,与其他文件一样,sys.stdin
也是可迭代的,
因此要迭代标准输入中的所有行,可像下面这样做:
import sys
for line in sys.stdin:
process(line)
另外,可对迭代器做的事情基本上都可对文件做,
如(使用 list(open(filename))
)将其转换为字符串列表,
其效果与使用 readlines
相同。
f = open('xx_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('xx_somefile.txt'))
lines
['First line\n', 'Second line\n', 'Third and final line\n']
first, second, third = open('xx_somefile.txt')
first
'First line\n'
second
'Second line\n'
third
'Third and final line\n'
在这个示例中,需要注意如下几点。
- 使用了
print
来写入文件,这将自动在提供的字符串后面添加换行符。 - 对打开的文件进行序列解包,从而将每行存储到不同的变量中。 (这种做法不常见,因为通常不知道文件包含多少行,但这演示了文件对象是可迭代的。)
- 写入文件后将其关闭,以确保数据得以写入磁盘。 (读取文件后并没有将其关闭。这可能有点粗糙,但并非致命的。)