2007-07-03
Python Cookbook 2.0 文件处理概述
前言:
文件处理是任何编程语言需要首先解决的问题之一,因为它太普遍了,良好的文件处理接口也是衡量一个好的编程语言的重要标准.
本章我们主要介绍了在Python中是如何进行文件处理的.在Python中,对文件操作支持可以分很多层:从最简单的内建的open函数,到标准库中提供的os模块,或者第三方的web类库,总之,Python提供了各种各样的方式来操作和处理文件.
基本操作:
在Python中, 一个文件对象属于内建类型file.内建函数open创建和返回一个文件对象.它的第一个参数,一个字符串,表示了文件的路径信息;第二个参数,也是一个字符串,表示代开文件的方式,如:
input = open('data', 'r')
output = open('/tmp/spam', 'w')
open接收的参数中,表示路径分隔的字符是斜杠'/', 而不用考虑具体的文件系统.在不使用斜杠区分路径的系统上(如windows),你也可以使用返斜杠'\',不过其实没有必要这样做.反斜杠在字符串中有特殊的含义,所以你需要使用两个返斜杠来表示它,如果在路径参数中没有给出路径信息,默认它在当前工作目录中.
对于第二个参数,打开模式,是用'r'表示从文本文件中读操作,这也是参数的默认值,所以open可以只使用一个参数来调用.而'rb'表示从二进制文件中进行读操作,'w'表示创建并写入一个文本文件,而'wb'表示创建并写入一个二进制文件.'r'模式有一个很好的变体'rU',它告诉Python读文本的时候,使用通用行结束符:这个意思是说可以不用考虑不同系统的行结束符转换,如unix系统或windows系统.
文本模式和二进制模式的区别在一些非类unix系统上是很重要的.当你使用二进制模式打开时,Python知道不需要考虑行结束符,它只是进行简单的从文件到内存中字符串的拷贝,而不做任何转换.当你在非类unix系统中使用文本模式打开文件时,Python知道要将'\n'字符替换为系统使用的换行符.所以你在Python中可以使用'\n'表示换行符,而不必考虑具体的字符是什么.
当你拥有了一个文件对象,你可以调用它的I/O操作的方法, 我们将在下面进行讨论.当你操作完文件的时候,要使用close方法关闭和文件的连接.
input.close()
在一些比较短小的代码中,用户经常忽略这个语句,因为当文件对象使用完毕后,Python的垃圾收集器会自动关闭文件连接.尽管如此,好的编程风格还是应该尽快的关闭文件一旦你使用完毕,尤其在大项目中,可能会出现很多不用的文件对象堆积在内存中的现象.另外 ,try/finally也很适合确保文件是否关闭,即使程序因为异常而中断.
当写文件的时候,使用write方法:
output.write(s)
s是一个字符串.当使用文本模式的时候,s是一个文本字符串,而使用二进制模式时,s是一个字节流.文件对象也有其它的写方法,如flush,写如缓存中的内容,还有writelines,一次写入一组字符串.然而,write是最常用的方法.
因为读文件比写文件更常见,相关的问题也更多, 所以文件对象包含的读方法比写方法要多.readline方法返回文件的下一行 ,可以参考下面的循环:
while True:
line = input.readline( )
if not line: break
process(line)
这样的代码曾经是理想的Python代码,可是现在不是了.另一种过时的方法是使用readline返回一个列表来处理:
for line in input.readlines( ):
process(line)
readline仅当内存能容纳文件内容时才有效,如果文件非常的大,readline可能会失败或者非常的慢(操作系统使用虚拟内存的时候,必须将内存的内容写到硬盘上),在今天的Python中,仅需要对文件对象进行循环处理就可以了:
for line in input:
process(line)
当然,也许你并不总是想一行一行的读文件,可能是想一次读文件中的部分或全部的字节,或者你用二进制模式打开一个文件, 用行操作就不合适了.在这种情况下,可以使用read方法.当没有参数的时候,read读出文件中剩下的所有内容,如果给一个整数参数N,它返回从当前位置的N个字节的内容,如果文件中剩下的内容数小于N,就返回文件剩下的部分.剩下的值得记住的方法是seek和tell,用于随机访问文件.这些方法常用于处理有固定长度记录组成的二进制文件.
可移植性和灵活性:
直观的看,Python对文件的支持是简单直接的.在学习本章的内容之前,先强调两点:代码可移植性和接口的灵活性.
首先需要记住的是Python的文件操作接口是完全可移植且与平台无关的.这一特性是非常重要的,比如,用Python写的在目录中搜索指定字符串的代码,是不需要修改就可以直接在各个平台上使用的,有了Python,底层的系统基本是不用考虑的.
另外,Python的文件操作工具也不仅仅只针对真是存在的文件,也适用于和文件对象暴露相同接口的对象,相这样,文件读方法仅仅考虑读,文件写方法仅仅考虑写.只要目标对象满足要求,一切没问题.
举例说明,你写了一个通用的读文件的方法,用于处理文本文件的每一行:
def scanner(fileobject, linehandler):
for line in fileobject:
linehandler(line)
如果你的Python程序使用这个代码来访问"目录"(sys.path)中的任意文件,都是没有问题的.下面写一个例子,打印出每一行的第一个单词:
from myutils import scanner
def firstword(line):
print line.split ( )[0]
file = open('data')
scanner(file, firstword)
到目前为止,一切都正常,我们写了一个通用的文件处理组件.可是需要注意的是,scanner中并没有包含类型信息,仅仅描述了一个对象被一行行的迭代处理.比如,你想处理一个字符串,而不是一个文件,StringIO模块,或者更快一些的cStringIO模块,都提供了类似的接口:
from cStringIO import StringIO
from myutils import scanner
def firstword(line): print line.split( )[0]
string = StringIO('one\ntwo xxx\nthree\n')
scanner(string, firstword)
StringIO对象和文件对象是兼容的.所以scanner处理了一个包含3行的内存中的对象,而不是一个真实的文件,你不需要修改scanner来实现自己的目的,甚至可以传递一个实现指定接口的对象:
class MyStream(object):
def _ _iter_ _(self):
# grab and return text from wherever
return iter(['a\n', 'b c d\n'])
from myutils import scanner
def firstword(line):
print line.split( )[0]
object = MyStream( )
scanner(object, firstword)
这次,scanner试图读取文件,实际上是调用对象的__iter__方法来实现.在实践中,用户可能从各种不同的渠道来获得信息:交互式程序,弹出的可视的输入框,一个 shelve对象,SQL数据库,XML或HTML,网络接口等等.重点是scanner不需要关心或考虑具体的对象是什么,或者说那个接口具体做了什么工作.
面向对象编程把这一类问题称为多态,对象的类型只有在进行操作时才判断,比如在scanner中的for循环.Python中的任何事物,对象接口,都不是具体的数据类型,而是耦合的单元.这样产生的效果是函数能在比你预料的更宽阔的范围中使用 ,如果你曾经使用过静态语言如C或者C++,这一点尤其明显.好像我们免费使用了C++中的模板.代码本身就具有灵活性,因为Python是动态类型的.
Python代码的可移植性和灵活性当然也不会限制Python中的文件处理,它继承了这两个特征.Python的其它特点,如代码的简洁和好的可读性,也帮助改变了传统的文件处理,在这里我们不一一列举Python的众多优点,希望读者在学习中能自己体会.
相关说明:
shelve: a "shelf" is a persistent, dictionary-like object.
文件处理是任何编程语言需要首先解决的问题之一,因为它太普遍了,良好的文件处理接口也是衡量一个好的编程语言的重要标准.
本章我们主要介绍了在Python中是如何进行文件处理的.在Python中,对文件操作支持可以分很多层:从最简单的内建的open函数,到标准库中提供的os模块,或者第三方的web类库,总之,Python提供了各种各样的方式来操作和处理文件.
基本操作:
在Python中, 一个文件对象属于内建类型file.内建函数open创建和返回一个文件对象.它的第一个参数,一个字符串,表示了文件的路径信息;第二个参数,也是一个字符串,表示代开文件的方式,如:
input = open('data', 'r')
output = open('/tmp/spam', 'w')
open接收的参数中,表示路径分隔的字符是斜杠'/', 而不用考虑具体的文件系统.在不使用斜杠区分路径的系统上(如windows),你也可以使用返斜杠'\',不过其实没有必要这样做.反斜杠在字符串中有特殊的含义,所以你需要使用两个返斜杠来表示它,如果在路径参数中没有给出路径信息,默认它在当前工作目录中.
对于第二个参数,打开模式,是用'r'表示从文本文件中读操作,这也是参数的默认值,所以open可以只使用一个参数来调用.而'rb'表示从二进制文件中进行读操作,'w'表示创建并写入一个文本文件,而'wb'表示创建并写入一个二进制文件.'r'模式有一个很好的变体'rU',它告诉Python读文本的时候,使用通用行结束符:这个意思是说可以不用考虑不同系统的行结束符转换,如unix系统或windows系统.
文本模式和二进制模式的区别在一些非类unix系统上是很重要的.当你使用二进制模式打开时,Python知道不需要考虑行结束符,它只是进行简单的从文件到内存中字符串的拷贝,而不做任何转换.当你在非类unix系统中使用文本模式打开文件时,Python知道要将'\n'字符替换为系统使用的换行符.所以你在Python中可以使用'\n'表示换行符,而不必考虑具体的字符是什么.
当你拥有了一个文件对象,你可以调用它的I/O操作的方法, 我们将在下面进行讨论.当你操作完文件的时候,要使用close方法关闭和文件的连接.
input.close()
在一些比较短小的代码中,用户经常忽略这个语句,因为当文件对象使用完毕后,Python的垃圾收集器会自动关闭文件连接.尽管如此,好的编程风格还是应该尽快的关闭文件一旦你使用完毕,尤其在大项目中,可能会出现很多不用的文件对象堆积在内存中的现象.另外 ,try/finally也很适合确保文件是否关闭,即使程序因为异常而中断.
当写文件的时候,使用write方法:
output.write(s)
s是一个字符串.当使用文本模式的时候,s是一个文本字符串,而使用二进制模式时,s是一个字节流.文件对象也有其它的写方法,如flush,写如缓存中的内容,还有writelines,一次写入一组字符串.然而,write是最常用的方法.
因为读文件比写文件更常见,相关的问题也更多, 所以文件对象包含的读方法比写方法要多.readline方法返回文件的下一行 ,可以参考下面的循环:
while True:
line = input.readline( )
if not line: break
process(line)
这样的代码曾经是理想的Python代码,可是现在不是了.另一种过时的方法是使用readline返回一个列表来处理:
for line in input.readlines( ):
process(line)
readline仅当内存能容纳文件内容时才有效,如果文件非常的大,readline可能会失败或者非常的慢(操作系统使用虚拟内存的时候,必须将内存的内容写到硬盘上),在今天的Python中,仅需要对文件对象进行循环处理就可以了:
for line in input:
process(line)
当然,也许你并不总是想一行一行的读文件,可能是想一次读文件中的部分或全部的字节,或者你用二进制模式打开一个文件, 用行操作就不合适了.在这种情况下,可以使用read方法.当没有参数的时候,read读出文件中剩下的所有内容,如果给一个整数参数N,它返回从当前位置的N个字节的内容,如果文件中剩下的内容数小于N,就返回文件剩下的部分.剩下的值得记住的方法是seek和tell,用于随机访问文件.这些方法常用于处理有固定长度记录组成的二进制文件.
可移植性和灵活性:
直观的看,Python对文件的支持是简单直接的.在学习本章的内容之前,先强调两点:代码可移植性和接口的灵活性.
首先需要记住的是Python的文件操作接口是完全可移植且与平台无关的.这一特性是非常重要的,比如,用Python写的在目录中搜索指定字符串的代码,是不需要修改就可以直接在各个平台上使用的,有了Python,底层的系统基本是不用考虑的.
另外,Python的文件操作工具也不仅仅只针对真是存在的文件,也适用于和文件对象暴露相同接口的对象,相这样,文件读方法仅仅考虑读,文件写方法仅仅考虑写.只要目标对象满足要求,一切没问题.
举例说明,你写了一个通用的读文件的方法,用于处理文本文件的每一行:
def scanner(fileobject, linehandler):
for line in fileobject:
linehandler(line)
如果你的Python程序使用这个代码来访问"目录"(sys.path)中的任意文件,都是没有问题的.下面写一个例子,打印出每一行的第一个单词:
from myutils import scanner
def firstword(line):
print line.split ( )[0]
file = open('data')
scanner(file, firstword)
到目前为止,一切都正常,我们写了一个通用的文件处理组件.可是需要注意的是,scanner中并没有包含类型信息,仅仅描述了一个对象被一行行的迭代处理.比如,你想处理一个字符串,而不是一个文件,StringIO模块,或者更快一些的cStringIO模块,都提供了类似的接口:
from cStringIO import StringIO
from myutils import scanner
def firstword(line): print line.split( )[0]
string = StringIO('one\ntwo xxx\nthree\n')
scanner(string, firstword)
StringIO对象和文件对象是兼容的.所以scanner处理了一个包含3行的内存中的对象,而不是一个真实的文件,你不需要修改scanner来实现自己的目的,甚至可以传递一个实现指定接口的对象:
class MyStream(object):
def _ _iter_ _(self):
# grab and return text from wherever
return iter(['a\n', 'b c d\n'])
from myutils import scanner
def firstword(line):
print line.split( )[0]
object = MyStream( )
scanner(object, firstword)
这次,scanner试图读取文件,实际上是调用对象的__iter__方法来实现.在实践中,用户可能从各种不同的渠道来获得信息:交互式程序,弹出的可视的输入框,一个 shelve对象,SQL数据库,XML或HTML,网络接口等等.重点是scanner不需要关心或考虑具体的对象是什么,或者说那个接口具体做了什么工作.
面向对象编程把这一类问题称为多态,对象的类型只有在进行操作时才判断,比如在scanner中的for循环.Python中的任何事物,对象接口,都不是具体的数据类型,而是耦合的单元.这样产生的效果是函数能在比你预料的更宽阔的范围中使用 ,如果你曾经使用过静态语言如C或者C++,这一点尤其明显.好像我们免费使用了C++中的模板.代码本身就具有灵活性,因为Python是动态类型的.
Python代码的可移植性和灵活性当然也不会限制Python中的文件处理,它继承了这两个特征.Python的其它特点,如代码的简洁和好的可读性,也帮助改变了传统的文件处理,在这里我们不一一列举Python的众多优点,希望读者在学习中能自己体会.
相关说明:
shelve: a "shelf" is a persistent, dictionary-like object.
标签: Python