任务
文件中读取文本或数据。
解决方案
最方便的方法是一次性读取文件中的所有内容并放置到一个大字符串中:
python">all_the_text = open('thefile.txt').read()#文本文件中的所有文本
all_the_data = open('abinfile','rb').read() #二进制文件中的所有数据
为了安全起见,最好还是给打开的文件对象指定一个名字,这样在完成操作之后可以迅速关闭文件,防止一些无用的文件对象占用内存。举个例子,对文本文件读取:
python">file_object = open('thefile.txt')try:
all_the_text = file_object.read()finally:
file_object.close()
不一定要在这里用 Try/fnally语句,但是用了效果更好,因为它可以保证文件对象被关闭,即使在读取中发生了严重错误。
最简单、最快,也最具 Python 风格的方法是逐行读取文本文件内容,并将读取的数据放置到一个字符串列表中:
python">list_of_all_the_lines = file_object.readlines()
这样读出的每行文本末尾都带有“m”符号;如果你不想这样,还有另一个替代的办法,比如:
python">list_of_all_the_lines = file_object.read().splitlines()
list_of_all_the_lines = file_object.read().split('\n')
list_of_all_the_lines = [L.rstrip('\n') for L in file_object]
最简单最快的逐行处理文本文件的方法是,用一个简单的 for循环语句:
python">for line in file_object:process line
这种方法同样会在每行末尾留下“m”符号;可以在for 循环的主体部分加一句:
python">line = line.rstrip('\n')
或者,你想去除每行的末尾的空白符(不只是””),常见的办法是:
python">line = line.rstrip()
讨论
除非要读取的文件非常巨大,不然一次性读出所有内容放进内存并进一步处理是最快和最方便的办法。内建函数open创建了一个Python的文件对象(另外,也可以通过调用内建类型fle创建文件对象)。你对该对象调用read方法将读出所有内容(无论是文本还是二进制数据),并放入一个大字符串中。如果内容是文本,可以选择用split方法或者更专用的splitlines将其切分成一个行列表。由于切分字符串到单行是很常见的需求,还可以直接对文件对象调用readlines,进行更方便更快速的处理。
可以直接对文件对象应用循环语句,或者将它传递给一个需要可选代对象的处理者,比如 list 或者 max。当它被当做一个可迭代对象处理时,一个被打开并被读取的文件对象中的每一个文本行都变成了迭代子项(因此,这也只适用于文本文件)。这种逐行迭代的处理方式很节省内存资源,速度也不错。
在 UNIX 或者类 UNIX 系统中,比如 Linux,Mac OsX,或者其他 BSD 变种,文本文件和二进制文件其实并没有什么区别。在 Windows 和老的 Macintosh 系统中,换行符不是标准的 ‘\n’,而分别是 '\r\n’和 ‘\r’。Python 会帮助你把这些换行符转化成 ‘\n’。这意味着当你打开二进制文件时,需要明确告诉Python,这样它就不会做任何转化。为了达到这个目的,必须传递 'rb’给 open 的第二个参数。在类 UNLX平台上,这么做也不会有什么坏处,而且总是区分文本文件和二进制文件是一个好习惯,当然在那些平台上这并不是强制性的要求。不过这些好习惯会让你的程序具有更好的可读性,也更易于理解,同时还能具有更好的平台兼容性。
如果不确定某文本文件会用什么样的换行符,可以将 open 的第二个参数设定为 'rU",指定通用换行符转化。这让你可以自由地在 Windows、UNIX(包括 Mac Os X),以及其他的老 Macintosh 平台上交换文件,完全不用担心任何问题:无论你的代码在什么平台上运行,各种换行符都被映射成 ‘\n’。
可以对 open 函数产生的文件对象直接调用read 方法,如解决方案中给出的第一个代码片段所示。当你这么做的时候,你在完成读取的同时,也失去了对那个文件对象的引用。在实践中,Python注意到了这种当场即时失去引用的情况,它会迅速关闭该文件。然而,更好的办法仍然是给 open 产生的结果指定一个名字,这样当你完成了处理,可以显式地自行关闭该文件。这能够确保该文件处于被打开状态的时间尽量的短,即使是在 Jython,IronPython 或其他变种Python平台上(这些平台的高级垃圾回收机制可能会推迟自动回收,不像现在的基于C的Python平台,CPython 会立刻执行回收)。为了确保文件对象即使在处理过程发生错误的情况下仍能够正确关闭,应该使用try/finally 语句,这是一种稳健而严谨的处理方式。
python">file_object =open('thefile.txt')
try:for line in file_obiect:process line
finally:file_obiect.close()
注意,不要把对 open的调用放入到try/finally语句的try子句中(这是初学者很常见的错误)。如果在打开文件的时候就发生了错误,那就没有什么东西需要关闭,而且,也没有什么实质性的东西绑定到了fileobiect这个名字上,当然也就不应该调用fle obiect.close()
如果选择一次读取文件的一小部分,而不是全部,方式就有点不同了。下面给出一个例子,一次读取一个二进制文件的100个字节,一直读到文件末尾:
python">file_object =open('abinfile''rb')
try:while True:chunk = file_obiect.read(100)if not chunk:breakdo_something_with(chunk)
finally:file_object.close()
给read 方法传人一个参数N,确保了read方法只读取下N个字节(或更少,如果读取位置已经很接近文件末尾的话)。当抵达文件末尾时,read返回空字符串。复杂的循环最好被封装成可复用的生成器(generator)。对于这个例子,我们只能将其逻辑的一部分进行封装,这是因为生成器(generator)的yield关键字不被允许出现在try/finally 语句的 try 子句中。如果要抛弃 try/finally 语句对文件关闭的保护,我们可以这么做:
python">def read_file_by_chunks(filename, chunksize=100):file_object = open(filename,*rb')while True:chunk = file_object.read(chunksize)if not chunk:breakyield chunkfile_object.close()
一旦 read_file_by_chunks生成器完成,以固定长度读取和处理二进制文件的代码就可以写得极其简单:
python">for chunk in read_file_by_chunks('abinfile'):do_something_with(chunk)
逐行读取文本文件的任务更为常见。只需对文件对象应用循环语句,如下:
python">for line in open('thefile.txt','rU'):do_something_with(line)
为了 100%确保完成操作之后没有无用的已打开的文件对象存在,可以将上述代码修改得更加严密稳固:
python">file_object =open('thefile.txt','rU'):
try:for line in file_object:do_something _with(line)
finally:file_object.close()