6.4.2 文件操作

常用计算机的人都知道,许多应用软件(如 Word、媒体播放器等)都需要处理文件, 并且都需要经过打开文件、读写文件、关闭文件的步骤,这其实是程序设计中文件处理的一 般过程的反映。

打开文件

在读写文件之前首先需要“打开”文件,这个步骤可以简单地理解为对磁盘文件进行必 要的初始化,至于其底层细节则无需了解。

Python 提供了函数 open 用于文件打开,用法如下:

  1. f = open(<文件名>,<打开方式>)

其含义是按指定的<打开方式>打开由<文件名>标识的磁盘文件,创建一个文件对象作为函 数的返回值,并使变量 f 引用这个文件对象。常用的打开方式包括”r”和”w”,它们分别表示 “读”方式和“写”方式。

顺便强调一下,Python 中的文件处理是面向对象风格的,即文件是一个对象,通过文 件对象的方法来实现文件操作。我们在第 5 章中初步介绍了对象概念,并且将在第 7 章详细 讨论面向对象。

为了读取一个文件的内容,需要以读方式打开文件。例如:

  1. f = open("oldfile.dat","r")

成功执行后,就可以通过文件对象 f 来读取文件 oldfile.dat 的内容了。若指定的文件不存在, 则 Python 将报错(IOError)。

为了向一个文件中写入内容,需要以写方式打开文件。例如:

  1. f = open("newfile.txt","w")

成功执行后,就可以通过文件对象 f 来向文件 oldfile.dat 中写入内容了。注意,以写方式打 开文件时,如果指定的文件不存在,则创建该文件;如果指定的文件已经存在,则会清除该 文件原来的内容,即相当于创建新文件。所以,以写方式打开文件时一定要小心,不要把现 有文件破坏了。

读文件

在介绍文件读写之前,先要理解文件“当前读写位置”的概念。读者应该了解老式的录 放机的录放过程吧:录放机有一个磁头,用于读取或录入磁带信息,随着磁带的转动,磁头 也就不断改变着录放位置。Python 中的文件采用类似的顺序读写过程:打开文件后,当前 读写位置就是文件开始处;随着读写命令的执行,当前读写位置不断改变,直至到达文件末 尾。

Python 中的文件对象提供了 read()、readline()和 readlines()方法用于读取文件内容。 read()的用法如下:

  1. <变量> = <文件对象>.read()

含义是读取从当前位置直到文件末尾的内容,并作为字符串返回。如果是刚打开的文件对象, 则返回的字符串包含文件的所有内容。

read()方法也可以带有参数:

  1. <变量> = <文件对象>.read(n)

含义是读取从当前位置开始的 n 个字符,并以此字符串作为返回值。如果指定的 n 大于文件中从当前位置到末尾的字符数,则仅返回这些字符。如果当前位置已到达文件末尾,则 read 返回空串。

假设有一个文件 rhyme.txt,其文本内容是:

  1. Good, better, best, Never let it rest, Till good is better, And better, best.

下面的语句序列对此文件进行读取

  1. >>> f = open("rhyme.txt","r")
  2. >>> s = f.read(8)
  3. >>> s
  4. 'Good, be'
  5. >>> f.read(20)
  6. 'tter, best,\nNever le'
  7. >>> print f.read()
  8. t it rest,
  9. Till good is better, And better, best.
  10. >>> f.close()

readline()的用法如下:

  1. <变量> = <文件对象>.readline()

含义是读取从当前位置到行末(即下一个换行字符)的所有字符,并以此字符串作为返回值, 赋值给变量。通常用此方法来读取文件的当前行。如果当前处于文件末尾,则 readline 返回 空串。例如:

  1. >>> f = open("rhyme.txt","r")
  2. >>> s = f.readline()
  3. >>> s
  4. 'Good, better, best,\n'
  5. >>> f.readline()
  6. 'Never let it rest,\n'
  7. >>> print f.readline()
  8. Till good is better,
  9. >>> f.close()

readlines()的用法如下:

  1. <变量> = <文件对象>.readlines()

其含义是读取从当前位置直到文件末尾的所有行,并将这些行构成一个字符串列表作为返回 值,列表中的每个元素都是文件的一行。如果当前处于文件末尾,则 readlines 返回空列表。 例如:

  1. >>> f = open("rhyme.txt","r")
  2. >>> f.readline()
  3. 'Good, better, best,\n'
  4. >>> f.readline()
  5. 'Never let it rest,\n'
  6. >>> f.readlines()
  7. ['Till good is better,\n', 'And better, best.\n']
  8. >>> f.readlines()
  9. []

写文件

当文件以写方式打开时,可以向文件中写入文本内容。与读文件一样,写入位置也是由 “当前读写位置”决定的。Python 文件对象提供两种写文件的方法:

  1. <文件对象>.write(<字符串>)
  2. <文件对象>.writelines(<字符串列表>)

其中,write 的含义是在文件当前位置处写入字符串,writelines 的含义是在文件当前位置处依次写入列表中的所有字符串。 下面的语句序列创建了一个新文件,并向其中写入了李白的名诗:

  1. >>> f = open("d:/libai.txt","w")
  2. >>> f.write("窗前明月光")
  3. >>> f.write("疑是地上霜\n")
  4. >>> f.write("举头望明月\n 低头思故乡")
  5. >>> f.close()

注意每一次 f.write()都是紧接着上次写入的内容继续的,并不会因为是另一条 f.write()就另 起一行。为了写多行文本,必须人工添加换行字符“\n”。那么,上述语句序列所创建的文 件 libai.txt 有几行文本呢?没错,只有 3 行,因为第一次调用 f.write 时并没有写入换行符, 这导致诗的前两句被写在同一行上了。如图 6.2 所示。

6.4.2 文件操作 - 图1

图 6.2 写入多行文本

再次强调,写方式打开文件会导致要么创建一个新文件,要么清除一个旧文件,总之文件的内容是全新的。那么有没有办法在现有文件内容基础上再写入一些新内容呢?答案是肯 定的。Python 还提供一种文件打开方式”a”,表示“追加”。以追加方式打开文件后,当前位 置被定位在文件末尾,可以继续写入文本而不改变原有的文件内容。例如:

  1. >>> f = open("d:/libai.txt","a")
  2. >>> f.write("\n---- 李白《静夜思》")
  3. >>> f.close()

结果如图 6.3 所示。

6.4.2 文件操作 - 图2

图 6.3 向文件追加写入内容

关闭文件

文件处理结束后需要关闭文件,这个步骤大体上涉及释放分配给文件的系统资源,以便 分配给其他文件使用。通过调用文件对象的 close 方法来关闭文件:

  1. <文件对象>.close()

注意,即使程序中没有关闭文件,Python 程序结束时也会自动关闭所有打开的文件。

然而好的做法是由程序自己关闭文件,否则有可能因程序意外终止而导致文件数据丢失。例 如,以写方式打开文件时,如果向文件中写入了文本但还没有关闭文件,那么所写内容是不 会存盘的。这时再以读方式打开同一文件,read()命令返回的是空串。下面的语句序列演示 了这种情况。

  1. >>> f = open("d:/test","w")
  2. >>> f.write("some words")
  3. >>> g = open("d:/test","r")
  4. >>> g.read()
  5. ''
  6. >>> f.close()
  7. >>> g.seek(0)
  8. >>> g.read()
  9. 'some words'

所以,强烈建议读者在程序中一旦结束对文件的读写,就立即关闭文件。

文件处理程序的常见结构

许多应用程序的算法结构都属于直接了当的 IPO(输入-处理-输出)模式,当输入输 出都是文件时,程序的结构大体如下:

  1. infile = open("input.dat","r")
  2. outfile = open("output.dat","w")
  3. while True:
  4. text = infile.readline()
  5. if text == "":
  6. break
  7. do something with text ...
  8. outfile.write(data)
  9. infile.close()
  10. outfile.close()

此代码的核心是一个 while 循环,循环的每一步利用 readline()读取输入文件的一行,然后对该行进行处理,并将处理结果写入输出文件。当某次循环读到空行(视为文件尾),则利用 break 跳出循环体,从而结束对文件的处理。

除了“while 循环+readline()”的结构,还可以利用“for 循环+readlines()”的结构。readlines() 一次性读出所有行,形成一个列表,然后针对这个列表进行循环。

  1. for line in infile.readlines():
  2. do something with line
  3. ...

实际上,Python 语言甚至允许直接将打开的文件与 for 循环结合使用,达到和“for 循 环+readlines()”同样的效果。代码如下:

  1. infile = open("input.dat","r")
  2. for line in infile:
  3. do something with line
  4. ...

这种用法有个好处是无需考虑内存大小,而 readlines()要求内存足够大,以便容纳它返回的 列表。

向文件追加数据

前述读方式打开的文件只能读取不能写入,写方式打开的文件是新建文件(写打开现存文件的话将清除内容),只能写入不能读取。有没有办法保留现存文件的内容并加入新内容 呢?

一种做法是先将文件的现有数据利用 readlines()读出来存入一个列表,然后向该列表添 加数据,最后再把新列表写入文件。这种做法对小文件没有问题,但当文件大小为数百 MB 或若干 GB 时,为了保存所有行的列表需要消耗大量内存。

其实 Python 还提供了一种打开方式”a”,称为“追加”方式,可以用于在现存文件的尾 部追加新数据。当然,如果请求打开的文件不存在,”a”方式就和”w”方式一样,创建一个新 文件。下面的语句演示了追加方式的用法:

  1. >>> f = open("oldfile.txt","a")
  2. >>> f.write("something new\n")
  3. >>> f.close()