6.4.2 文件操作
常用计算机的人都知道,许多应用软件(如 Word、媒体播放器等)都需要处理文件, 并且都需要经过打开文件、读写文件、关闭文件的步骤,这其实是程序设计中文件处理的一 般过程的反映。
打开文件
在读写文件之前首先需要“打开”文件,这个步骤可以简单地理解为对磁盘文件进行必 要的初始化,至于其底层细节则无需了解。
Python 提供了函数 open 用于文件打开,用法如下:
f = open(<文件名>,<打开方式>)
其含义是按指定的<打开方式>打开由<文件名>标识的磁盘文件,创建一个文件对象作为函 数的返回值,并使变量 f 引用这个文件对象。常用的打开方式包括"r"和"w",它们分别表示 “读”方式和“写”方式。
顺便强调一下,Python 中的文件处理是面向对象风格的,即文件是一个对象,通过文 件对象的方法来实现文件操作。我们在第 5 章中初步介绍了对象概念,并且将在第 7 章详细 讨论面向对象。
为了读取一个文件的内容,需要以读方式打开文件。例如:
f = open("oldfile.dat","r")
成功执行后,就可以通过文件对象 f 来读取文件 oldfile.dat 的内容了。若指定的文件不存在, 则 Python 将报错(IOError)。
为了向一个文件中写入内容,需要以写方式打开文件。例如:
f = open("newfile.txt","w")
成功执行后,就可以通过文件对象 f 来向文件 oldfile.dat 中写入内容了。注意,以写方式打 开文件时,如果指定的文件不存在,则创建该文件;如果指定的文件已经存在,则会清除该 文件原来的内容,即相当于创建新文件。所以,以写方式打开文件时一定要小心,不要把现 有文件破坏了。
读文件
在介绍文件读写之前,先要理解文件“当前读写位置”的概念。读者应该了解老式的录 放机的录放过程吧:录放机有一个磁头,用于读取或录入磁带信息,随着磁带的转动,磁头 也就不断改变着录放位置。Python 中的文件采用类似的顺序读写过程:打开文件后,当前 读写位置就是文件开始处;随着读写命令的执行,当前读写位置不断改变,直至到达文件末 尾。
Python 中的文件对象提供了 read()、readline()和 readlines()方法用于读取文件内容。 read()的用法如下:
<变量> = <文件对象>.read()
含义是读取从当前位置直到文件末尾的内容,并作为字符串返回。如果是刚打开的文件对象, 则返回的字符串包含文件的所有内容。
read()方法也可以带有参数:
<变量> = <文件对象>.read(n)
含义是读取从当前位置开始的 n 个字符,并以此字符串作为返回值。如果指定的 n 大于文件中从当前位置到末尾的字符数,则仅返回这些字符。如果当前位置已到达文件末尾,则 read 返回空串。
假设有一个文件 rhyme.txt,其文本内容是:
Good, better, best, Never let it rest, Till good is better, And better, best.
下面的语句序列对此文件进行读取
>>> f = open("rhyme.txt","r")
>>> s = f.read(8)
>>> s
'Good, be'
>>> f.read(20)
'tter, best,\nNever le'
>>> print f.read()
t it rest,
Till good is better, And better, best.
>>> f.close()
readline()的用法如下:
<变量> = <文件对象>.readline()
含义是读取从当前位置到行末(即下一个换行字符)的所有字符,并以此字符串作为返回值, 赋值给变量。通常用此方法来读取文件的当前行。如果当前处于文件末尾,则 readline 返回 空串。例如:
>>> f = open("rhyme.txt","r")
>>> s = f.readline()
>>> s
'Good, better, best,\n'
>>> f.readline()
'Never let it rest,\n'
>>> print f.readline()
Till good is better,
>>> f.close()
readlines()的用法如下:
<变量> = <文件对象>.readlines()
其含义是读取从当前位置直到文件末尾的所有行,并将这些行构成一个字符串列表作为返回 值,列表中的每个元素都是文件的一行。如果当前处于文件末尾,则 readlines 返回空列表。 例如:
>>> f = open("rhyme.txt","r")
>>> f.readline()
'Good, better, best,\n'
>>> f.readline()
'Never let it rest,\n'
>>> f.readlines()
['Till good is better,\n', 'And better, best.\n']
>>> f.readlines()
[]
写文件
当文件以写方式打开时,可以向文件中写入文本内容。与读文件一样,写入位置也是由 “当前读写位置”决定的。Python 文件对象提供两种写文件的方法:
<文件对象>.write(<字符串>)
<文件对象>.writelines(<字符串列表>)
其中,write 的含义是在文件当前位置处写入字符串,writelines 的含义是在文件当前位置处依次写入列表中的所有字符串。 下面的语句序列创建了一个新文件,并向其中写入了李白的名诗:
>>> f = open("d:/libai.txt","w")
>>> f.write("窗前明月光")
>>> f.write("疑是地上霜\n")
>>> f.write("举头望明月\n 低头思故乡")
>>> f.close()
注意每一次 f.write()都是紧接着上次写入的内容继续的,并不会因为是另一条 f.write()就另 起一行。为了写多行文本,必须人工添加换行字符“\n”。那么,上述语句序列所创建的文 件 libai.txt 有几行文本呢?没错,只有 3 行,因为第一次调用 f.write 时并没有写入换行符, 这导致诗的前两句被写在同一行上了。如图 6.2 所示。
图 6.2 写入多行文本
再次强调,写方式打开文件会导致要么创建一个新文件,要么清除一个旧文件,总之文件的内容是全新的。那么有没有办法在现有文件内容基础上再写入一些新内容呢?答案是肯 定的。Python 还提供一种文件打开方式"a",表示“追加”。以追加方式打开文件后,当前位 置被定位在文件末尾,可以继续写入文本而不改变原有的文件内容。例如:
>>> f = open("d:/libai.txt","a")
>>> f.write("\n---- 李白《静夜思》")
>>> f.close()
结果如图 6.3 所示。
图 6.3 向文件追加写入内容
关闭文件
文件处理结束后需要关闭文件,这个步骤大体上涉及释放分配给文件的系统资源,以便 分配给其他文件使用。通过调用文件对象的 close 方法来关闭文件:
<文件对象>.close()
注意,即使程序中没有关闭文件,Python 程序结束时也会自动关闭所有打开的文件。
然而好的做法是由程序自己关闭文件,否则有可能因程序意外终止而导致文件数据丢失。例 如,以写方式打开文件时,如果向文件中写入了文本但还没有关闭文件,那么所写内容是不 会存盘的。这时再以读方式打开同一文件,read()命令返回的是空串。下面的语句序列演示 了这种情况。
>>> f = open("d:/test","w")
>>> f.write("some words")
>>> g = open("d:/test","r")
>>> g.read()
''
>>> f.close()
>>> g.seek(0)
>>> g.read()
'some words'
所以,强烈建议读者在程序中一旦结束对文件的读写,就立即关闭文件。
文件处理程序的常见结构
许多应用程序的算法结构都属于直接了当的 IPO(输入-处理-输出)模式,当输入输 出都是文件时,程序的结构大体如下:
infile = open("input.dat","r")
outfile = open("output.dat","w")
while True:
text = infile.readline()
if text == "":
break
do something with text ...
outfile.write(data)
infile.close()
outfile.close()
此代码的核心是一个 while 循环,循环的每一步利用 readline()读取输入文件的一行,然后对该行进行处理,并将处理结果写入输出文件。当某次循环读到空行(视为文件尾),则利用 break 跳出循环体,从而结束对文件的处理。
除了“while 循环+readline()”的结构,还可以利用“for 循环+readlines()”的结构。readlines() 一次性读出所有行,形成一个列表,然后针对这个列表进行循环。
for line in infile.readlines():
do something with line
...
实际上,Python 语言甚至允许直接将打开的文件与 for 循环结合使用,达到和“for 循 环+readlines()”同样的效果。代码如下:
infile = open("input.dat","r")
for line in infile:
do something with line
...
这种用法有个好处是无需考虑内存大小,而 readlines()要求内存足够大,以便容纳它返回的 列表。
向文件追加数据
前述读方式打开的文件只能读取不能写入,写方式打开的文件是新建文件(写打开现存文件的话将清除内容),只能写入不能读取。有没有办法保留现存文件的内容并加入新内容 呢?
一种做法是先将文件的现有数据利用 readlines()读出来存入一个列表,然后向该列表添 加数据,最后再把新列表写入文件。这种做法对小文件没有问题,但当文件大小为数百 MB 或若干 GB 时,为了保存所有行的列表需要消耗大量内存。
其实 Python 还提供了一种打开方式"a",称为“追加”方式,可以用于在现存文件的尾 部追加新数据。当然,如果请求打开的文件不存在,"a"方式就和"w"方式一样,创建一个新 文件。下面的语句演示了追加方式的用法:
>>> f = open("oldfile.txt","a")
>>> f.write("something new\n")
>>> f.close()