之前介绍过的内容都是和解释器自带的数据结构打交道。我们的程序与外部的交互只是通过input、raw_input和print函数,与外部的交互很少。本章将介绍文件和流,可以让你在程序调用期间存储数据,并且可以处理来自其它程序的数据。
5.1 打开文件
open函数用来打开文件,语法如下:
open(name[,mode[,buffering]])
open函数使用一个文件名作为唯一的强制参数,然后返回一个文件对象。模式(mode)和缓冲(buffering)参数都是可选的。
5.1.1 文件模式(mode)
如果open函数只带一个文件名参数,我们就可以获得能读取文件内容的文件对象。如果要向文件内写入内容,则必须提供一个模式参数来显示声明。open函数中的模式参数只有几个值,如表5-1所示:
明确地指定读模式和什么模式参数都不用的效果是一样的。
>>> f = open(r'c:\text\somefile.txt') >>> f = open(r'c:\text\somefile1.txt') Traceback (most recent call last): FileNotFoundError: [Errno 2] No such file or directory: 'c:\\text\\somefile1.txt'
使用写模式可以向文件写入内容。
"+"参数可以用到其他任何模式中,指明读和写都是允许的。比如"r+"能打开一个文本文件用来读写时使用。
"b"模式改变处理文件的方法。Python假定处理的是文本文件。通常这样做不会有任何问题。但如果处理的是一些其他类型的文件(二进制文件),比如声音剪辑或者图像,那么应该在模式参数中增加'b'。参数'rb'可以用来读取一个二进制文件。
一般来说,在Python中,换行符(\n)表示结束一行并另起一行,这是Unix系统中的规范。在Windows中一行结束的标志是\r\n。为了在程序中隐藏这些区别(支持跨平台),Python做了一些自动转换:当在Windows下用文本模式读取文件中的文本时,Python将\r\n转换成\n。相反地,当在Windows下用文本模式向文件写入文本时,Python会把\n转换成\r\n。MAC系统的处理也是如此,只是转换在\r和\n之间进行。
通过在模式参数中使用U参数能够在打开文件时使用通用的换行符支持模式,在这种模式下,所有的换行符/字符串(\r\n,\r或者是\n)都被转换成\n,而不用考虑运行的平台。
5.1.2 缓冲
open函数的第3个参数(可选)控制着文件的缓存。如果参数是0(或者是False),I/O(输入/输出)就是无缓冲的(所有的读写操作都直接针对硬盘);如果是1(或者是True),I/O就是由缓存的(意味着Python使用内存在代替硬盘,让程序更快,只有使用flush或者close时才会更新硬盘上的数据)。大于1的数字代表缓冲区的大小(单位是字节),-1(或者是任何负数)代表使用默认的缓冲区大小。
5.2 基本文件方法
打开文件的方法已经介绍了,那么下一步就是用它们做些有用的事。接下来介绍文件对象(和一些类文件对象,有时称为流)的一些基本方法。
三种标准的流:
sys.stdin、sys.stdout、sys.stderr,它们实际上是文件(或者是类文件对象):大部分文件对象可用的操作它们也可以使用。
数据输入的标准源是sys.stdin。当程序从标准输入读取数据时,可以通过输入或者使用管道把它和其他程序的标准输出链接起来提供文本。
要打印的文件保存在sys.stdout内。input和raw_input的函数的提示文字也是写入在sys.stdout中的。写入sys.stdout的数据一般是出现在屏幕上,但也能使用管道连接到其他程序的标准输入。
错误信息被写入sys.stderr。
5.2.1 读和写
文件(或流)最重要的能力是提供或者接受数据。如果有一个名为f的类文件对象,就可以使用f.write和f.read的方法写入和读取数据。
每次调用f.write(string)时,所提供的参数string会被追加到文件中已存在部分的后面。
>>> f = open("somefile.txt",'w') >>> f.write("Hello, ") 7 >>> f.write("World!") 6 >>> f.close()
在完成了对一个文件的操作时,调用close。这个方法会后续介绍。
读取只要记得告诉流要读多少字符(字节)即可。
>>> f = open("somefile.txt",'r') >>> f.read(4) #读取字符数"4" 'Hell' >>> f.read() #读取了剩下的 'o, World!'
5.2.2 管道输出
在Unix中使用管道可以在一个命令后续写其他的多个命令。
$ cat somefile.txt | python somescript.py | sort
管道符号将一个命令标准输出和下一个命令的标准输入连在一起。somescript.py会从cat somefile.txt输出的读取数据,并把结果写入它的sys.stdout(sort在此得到数据)中。
5.2.3 读写行
之前使用f.read和f.write来对文件对象实现读和取字符(字节),还可以使用f.readline读取单独的一行(从当前的位置开始一直一个换行符出现,也读取这个换行符)。不使用任何参数(一行被读取和返回)或者使用一个非负的整数作为readline可以读取的字符(或字节)的最大值。因此,如果somefile.readline()返回"Hello, World!\n",somefile.readline(5)返回'Hello'。readlines方法可以读取一个文件的所有行并将其作为列表返回。
writelines方法和readlines相反:传给它一个字符串列表,它会把所有的字符串写入文件(或流)。注意,程序不会增加新行,需要自己添加。没有writeline方法,因为能使用write。
5.2.4 关闭文件
使用close方法关闭文件。可以避免在某些操作系统或设置中进行无用的修改,这样做也会避免用完系统中所打开文件的配额。
写入过的文件总是应该关闭,是因为Python可能会缓存写入的数据,如果程序因为某些原因崩溃了,那么数据就不会被写入文件。
Python专门为这种情况设计的语句,即with语句:
with open("somefile.txt") as somefile: do_something(somefile)
with语句可以打开文件并且将其赋值到变量上(somefile)。之后就可以将数据写入语句体中的文件。文件在语句结束后会被自动关闭,即使是由于异常引起的结束也是如此。
上下文管理器:
with语句实际上是通用的结构,允许使用所谓的上下文管理器(context manager)。上下文管理器是一种支持_enter_和_exit_方法的对象。
_enter_方法不带参数,它在进入with语句块的时候被调用,返回值绑定到在as关键词之后的变量。
_exit_方法带有3个参数:异常类型、异常对象和异常回溯。在离开方法时这个函数被调用。如果_exit_返回false,那么所有的异常都不会被处理。
5.2.5 使用基本文件方法
假设somefile.txt包含代码所示的内容。可以进行如下的操作。
Welcome to this file There is nothing here except This stupid haiku
首先是read(n):
>>> f = open(r"c:\text\somefile.txt") >>> f.read(7) 'Welcome' >>> f.read(4) ' to ' >>> f.close()
然后是read():
>>> f = open(r"c:\text\somefile.txt") >>> print(f.read()) Welcome to this file There is nothing here except This stupid haiku >>> f.close()
接着是readline():
>>> f = open(r"c:\text\somefile.txt") for i in range(3): print(str(i) + ": " + f.readline()) 0: Welcome to this file 1: There is nothing here except 2: This stupid haiku >>> f.colse
以及readlines():
>>> import pprint >>> pprint.pprint(open(r"c:\text\somefile.txt").readlines()) ['Welcome to this file\n', 'There is nothing here except\n', 'This stupid haiku']
本例中,使用的是文件对象自动关闭的方式。
下面是写文件,首先是write(string):
>>> f = open(r"c:\text\somefile.txt","w") >>> f.write("this\nis no\nhaiku") 16 >>> f.close()
修改了文本文件,内容如下:
this is no haiku
最后是writelines(list):
>>> f = open(r"c:\text\somefile.txt") >>> lines = f.readlines() >>> f.close >>> lines[1] = "isn't a\n" >>> f = open(r"c:\text\somefile.txt","w") >>> f.writelines(lines) >>> f.close()
运行这个程序后,文件包含的文本如下:
this isn't a haiku
5.3 对文件内容进行迭代
前面介绍了文件对象的一些文本对象提供的方法,以及如何获取这样的文件对象。对文件内容进行迭代以及重复执行一些操作,是最常见的文件操作之一。尽管有很多方法可以实现这个功能,比如之前的(read、readline和readlines)。还有一些方法就是下面将要介绍的(比如xreadlines和文件迭代器)。
在这部分的所有例子中使用了名为process的虚拟函数,用来表示每个字符或每行的处理过程。
def process(string) print("Processing: ",string)
5.3.1 按字节处理
最常见的对文件内容进行迭代的是方法在while循环中使用read方法。例如,对每个字符(字节)进行循环,方法如下:
f = open(r"c:\text\somefile.txt") while True: char = f.read(1) if not char:break process(char) f.close()
5.3.2 按行操作
当处理文本文件时,经常会对文件的行进行迭代而不是处理单个字符。对行进行迭代是在while循环中使用readline方法:
f = open(r"c:\text\somefile.txt") while True: line = f.readline() if not line:break process(line) f.close()
5.3.3 读取所有内容
如果文件不是很大,那么可以使用不带参数的read方法一次读取整个文件(把整个文件当做一个字符串来读取),或者使用readlines方法(把文件读入一个字符串列表,在列表中每个字符串就是一行)。注意:将文件的内容读入一个字符串或者是读入列表在其他时候也很有用,读取后就可以对字符串使用之前的方法进行操作,也可以将行列表存入一些数据结构中,以备将来使用。
#用read迭代每个字符 f = open(r"c:\text\somefile.txt") for char in f.read(): process(char) f.close()
#用readlines迭代行 f = open(r"c:\text\somefile.txt") for line in f.readlines(): process(line) f.close()
5.3.4 使用fileinput实现懒惰行迭代
在需要对一个非常大的文件进行迭代行的操作时,readlines生成列表就会占用太多的内容。可以使用while循环和readline方法来替代。当然,在Python中如果能使用for循环,那么fileinput就是首选,读取实际需要的文件部分。fileinput模块包含了打开文件的函数,只需要传一个文件名给它。
import fileinput for line in fileinput.input(r"c:\text\somefile.txt"): process(line)
5.3.5 文件迭代器
在Python的近几个版本中,文件对象是可迭代的,这就意味着可以直接在for循环中使用它们,从而对它们的内容进行迭代。
f = open(r"c:\text\somefile.txt") for line in f: process(line) f.close()
对文件进行迭代而不使用变量存储文件对象
for line in open(r"c:\text\somefile.txt"): process(line)
注意:sys.stdin是可迭代的,就像其他的文件对象。因此如果想要迭代标准输入的所有行,可以按如下形式使用sys.stdin。
import sys for line in sys.stdin: process(line)
5.4 小结
本章中介绍了如何通过文件对象和类文件对象与环境互动,I/O也是Python中最重要的技术之一。
类文件对象。类文件对象是支持read和readline方法(write和writelines)的非正式对象。
打开和关闭文件。通过提供一个文件名,使用open函数打开一个文件。如果希望确保文件被正常关闭,即使发生错误时也是如此可以使用with语句。
模式和文件类型。当打开一个文件时,也可以提供一个模式,比如''代表读模式,''代表写模式。还可以将文件作为二进制文件打开(只在Python进行换行符转换的平台上才需要,比如Windows)
标准流。3个标准文件对象(在sys模块中的stdin、stdout和stderr)是一个类文件对象。
读和写。使用read或是write方法可以对文件对象或类文件对象进行读写操作。
读写行。使用readline和readlines和迭代可以从文件中读取行,使用writelines可以写入数据。
迭代文件内容。通过迭代文件对象本身就可以迭代文本中的行。
5.4.1 本章的新函数
新函数如表5-2所示。