第5章 Python文件的使用 在程序运行时,数据保存在内存的变量里。内存中的数据在程序结束或关机后就会消失。如果想要在下次开机运行程序时还使用同样的数据,就需要把数据存储在不易失的存储介质中,如硬盘、光盘或U盘里。不易失存储介质上的数据保存在以存储路径命名的文件中。通过读/写文件,程序就可以在运行时保存数据。在本章中,主要学习使用Python在磁盘上创建、读/写以及关闭文件。本章只讲述基本的文件操作函数,更多函数请参考Python标准文档。 5.1文件 视频讲解 简单地说,文件是由字节组成的信息,在逻辑上具有完整意义,通常在磁盘上永久保存。Windows系统的数据文件按照编码方式分为两大类: 文本文件和二进制文件。文本文件可以处理各种语言所需的字符,只包含基本文本字符,不包括诸如字体、字号、颜色等信息。它可以在文本编辑器和浏览器中显示。即在任何情况下,文本文件都是可读的。 使用其他编码方式的文件即二进制文件,如Word文档、PDF文件、图像和可执行程序等。如果用文本编辑器打开一个JPG文件或Word文档,会看到一堆乱码,如图51所示。也就是说,每一种二进制文件都需要自己的处理程序才能打开并操作。 图51文本编辑器打开JPG文件运行效果 在本章中,重点学习文本文件的操作。当然二进制文件的处理也可以使用Python提供的模块进行处理。 5.2文件的访问 视频讲解 对文件的访问是指对文件进行读/写操作。使用文件跟平时生活中使用记事本很相似。我们使用记事本时,需要先打开本子,使用后要合上它。打开记事本后,既可以读取信息,也可以向本子里写内容。不管哪种情况,都需要知道在哪里进行读/写。我们在记事本中既可以一页页从头到尾地读,也可以直接跳转到所需要的地方。使用文件工作也是一样。 在Python中对文件的操作通常按照以下三个步骤进行。 (1) 使用open()函数打开(或建立)文件,返回一个file对象。 (2) 使用file对象的读/写方法对文件进行读/写操作。其中,将数据从外存传输到内存的过程称为读操作,将数据从内存传输到外存的过程称为写操作。 (3) 使用file对象的close()方法关闭文件。 5.2.1打开(建立)文件 在Python中要访问文件,必须打开Python Shell与磁盘上文件之间的连接。当使用open()函数打开或建立文件时,会建立文件和使用它的程序之间的连接,并返回代表连接的文件对象。通过文件对象,就可以在文件所在磁盘和程序之间传递文件内容,执行文件上所有后续操作。文件对象有时也称为文件描述符或文件流。 当建立了Python程序和文件之间的连接后,就创建了“流”数据,如图52所示。通常程序使用输入流读出数据,使用输出流写入数据,就好像数据流入到程序并从程序中流出。打开文件后,才能读或写(或读并且写)文件内容。 图52输入输出流 open()函数用来打开文件。open()函数需要一个字符串路径,表明希望打开文件,并返回一个文件对象。语法如下: fileobj=open(filename[,mode[,buffering]]) 其中,fileobj是open()函数返回的文件对象。参数filename是文件名,是必写参数,它既可以是绝对路径,也可以是相对路径。mode(模式)和buffering(缓冲)可选。 mode是指明文件类型和操作的字符串,可以使用的值如表51所示。 表51open()函数中mode参数常用值 值描述 'r'读模式。如果文件不存在,则发生异常 'w '写模式。如果文件不存在,则创建文件再打开; 如果文件存在,则清空文件内容再打开 'a'追加模式。如果文件不存在,则创建文件再打开; 如果文件存在,则打开文件后将新内容追加至原内容之后 'b'二进制模式。可添加到其他模式中使用 '+'读/写模式。可添加到其他模式中使用 说明: (1) 当mode参数省略时,可以获得能读取文件内容的文件对象。即'r'是mode参数的默认值。 (2) '+'参数指明读和写都是允许的,可以用到其他任何模式中。如' r+'可以打开一个文本文件并读/写。 (3) 'b'参数改变处理文件的方法。通常,Python处理的是文本文件。当处理二进制文件时(如声音文件或图像文件),应该在模式参数中增加'b'。如可以用'rb'来读取一个二进制文件。 open()函数的第三个参数buffering控制缓冲。当参数取0或False时,输入输出(I/O)是无缓冲的,所有读/写操作直接针对硬盘。当参数取1或True时,I/O有缓冲,此时Python使用内存代替硬盘,使程序运行速度更快,只有使用flush或close时才会将数据写入硬盘。当参数大于1时,表示缓冲区的大小,以字节为单位; 负数表示使用默认缓冲区大小。 下面举例说明open()函数的使用。 先用记事本创建一个文本文件,取名为hello.txt。输入以下内容并保存在d:\Python中: Hello! HenanZhengzhou 在交互式环境中输入以下代码: helloFile=open("d:\\python\\hello.txt") 这条命令将以读取文本文件的方式打开放在D盘Python文件夹下的hello.txt文件。读模式是Python打开文件的默认模式。当文件以读模式打开时,只能从文件中读取数据而不能向文件写入或修改数据。 当调用open()函数时将返回一个文件对象,在本例中文件对象保存在helloFile变量中。 print helloFile _io.Text IOWrapper name='d:\\python\\hello.txt', mode='r' encoding='cp936' 打开文件对象时可以看到文件名、读/写模式和编码格式。cp936就是指Windows系统里第936号编码格式,即GB2312的编码。接下来就可以调用helloFile文件对象的方法读取文件中的数据了。 5.2.2读取文本文件 可以调用文件对象的多种方法读取文件内容。 1. read()方法 不设置参数的read()方法将整个文件的内容读取为一个字符串。read()方法一次读取文件的全部内容,性能根据文件大小而变化,如1GB的文件读取时需要使用同样大小的内存。 【例51】调用read()方法读取hello.txt文件中的内容。 helloFile=open("d:\\python\\hello.txt") fileContent=helloFile.read() helloFile.close() print(fileContent) 输出结果: Hello! Henan Zhengzhou 也可以设置最大读入字符数来限制read()函数一次返回的大小。 【例52】设置参数,一次从文件中读取三个字符。 helloFile=open("d:\\python\\hello.txt") fileContent="" while True: fragment=helloFile.read(3) if fragment=="": #或者 if not fragment break fileContent+=fragment helloFile.close() print(fileContent) 当读到文件结尾后,read()方法会返回空字符串,此时fragment==""成立,退出循环。 2. readline()方法 readline()方法从文件中获取一个字符串,这个字符串就是文件中的一行。 【例53】调用readline()方法读取hello.txt文件的内容。 helloFile=open("d:\\python\\hello.txt") fileContent="" while True: line=helloFile.readline() if line=="": #或者 if not line break fileContent+=line helloFile.close() print(fileContent) 当读取到文件结尾后,readline()方法同样返回空字符串,使得line==""成立,跳出循环。 3. readlines()方法 readlines()方法返回一个字符串列表,其中的每一项是文件中每一行的字符串。 【例54】使用readlines()方法读取文件内容。 helloFile=open("d:\\python\\hello.txt") fileContent=helloFile.readlines() helloFile.close() print(fileContent) for line in fileContent: #输出列表 print(line) readlines()方法也可以设置参数,指定一次读取的字符数。 5.2.3写文本文件 视频讲解 写文件与读文件相似,都需要先创建文件对象连接。所不同的是,打开文件时是以写模式或添加模式打开。如果文件不存在,则创建该文件。 与读文件时不能添加或修改数据类似,写文件时也不允许读取数据。写模式打开已有文件时,会覆盖文件原有内容,从头开始,就像用一个新值覆写一个变量的值。 例如: helloFile=open("d:\\python\\hello.txt","w") #写模式打开已有文件时会覆盖文件原有内容 fileContent=helloFile.read() Traceback (most recent call last): File "pyshell#1", line 1, in module fileContent=helloFile.read() IOError: File not open for reading helloFile.close() helloFile=open("d:\\python\\hello.txt") fileContent=helloFile.read() len(fileContent) 0 helloFile.close() 由于写模式打开已有文件,文件原有内容会被清空,所以再次读取内容时长度为0。 1. write()方法 write()方法将字符串参数写入文件。 【例55】用write()方法写文件。 helloFile=open("d:\\python\\hello.txt","w") helloFile.write("First line.\nSecond line.\n") helloFile.close() helloFile=open("d:\\python\\hello.txt","a") helloFile.write("third line. ") helloFile.close() helloFile=open("d:\\python\\hello.txt") fileContent=helloFile.read() helloFile.close() print(fileContent) 运行结果: First line. Second line. third line. 当以写模式打开文件hello.txt时,文件原有内容被覆盖。调用write方法将字符串参数写入文件,这里“\n”代表换行符。关闭文件之后再次以添加模式打开文件hello.txt,调用write()方法写入的字符串"third line."被添加到了文件末尾。最终以读模式打开文件后读取到的内容共有三行字符串。 注意: write()方法不能自动在字符串末尾添加换行符,需要自己添加“\n”。 【例56】完成一个自定义函数copy_file(),实现文件的复制功能。 copy_file()函数需要两个参数,指定需要复制的文件oldfile和文件的备份newfile。分别以读模式和写模式打开两个文件,从oldfile一次读入50个字符并写入newfile。当读到文件末尾时fileContent==""成立,退出循环并关闭两个文件。 def copy_file(oldfile,newfile): oldFile=open(oldfile,"r") newFile=open(newfile,"w") while True: fileContent=oldFile.read(50) if fileContent=="": #读到文件末尾时 break newFile.write(fileContent) oldFile.close() newFile.close() return copy_file("d:\\python\\hello.txt","d:\\python\\hello2.txt") 2. writelines()方法 writelines(sequence)方法向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。例如: obj = open("log.py","w") list02 = ["11","test","hello","44","55"] obj.writelines(list02) obj.close() 运行结果是生成一个log.py文件,内容是"11testhello4455",可见没有换行。 注意: writelines ()方法写入的序列必须是字符串序列,若是整数序列,则会产生错误。 5.2.4文件内移动 无论读或写文件,Python都会跟踪文件中的读/写位置。在默认情况下,文件的读/写都从文件的开始位置进行。Python提供了控制文件读/写起始位置的方法,使得我们可以改变文件读/写操作发生的位置。 当使用open()函数打开文件时,open()函数在内存中创建缓冲区,将磁盘上的文件内容复制到缓冲区。文件内容复制到文件对象缓冲区后, 图53文件当前位置 文件对象将缓冲区视为一个大的列表,其中的每一个元素都有自己的索引,文件对象按字节对缓冲区索引计数。同时,文件对象对文件当前位置,即当前读/写操作发生的位置进行维护,如图53所示。许多方法隐式使用当前位置。如调用readline()方法后,文件当前位置移动到下一个回车处。 Python使用一些函数跟踪文件当前位置。tell()函数可以计算文件当前位置和开始位置之间的字节偏移量。 exampleFile=open("d:\\python\\example.txt","w") exampleFile.write("0123456789") exampleFile.close() exampleFile=open("d:\\python\\example.txt") exampleFile.read(2) '01' exampleFile.read(2) '23' exampleFile.tell() 4L exampleFile.close() 这里exampleFile.tell()函数返回的是一个整数4,表示文件当前位置和开始位置之间有4字节的偏移量。因为已经从文件中读取4个字符了,所以有4字节偏移量。 seek()函数设置新的文件当前位置,允许在文件中跳转,实现对文件的随机访问。 seek()函数有两个参数: 第一个参数是字节数; 第二个参数是引用点。seek()函数将文件当前指针由引用点移动指定的字节数到指定的位置。语法如下: seek(offset[,whence]) 说明: offset是一个字节数,表示偏移量。引用点whence有如下三个取值。 文件开始处为0,也是默认取值。意味着使用该文件的开始处作为基准位置,此时字节偏移量必须非负。 当前文件位置为1。则是使用当前位置作为基准位置,此时偏移量可以取负值。 文件结尾处为2。则该文件的末尾将被作为基准位置。 注意: 当文件以文本文件方式打开时,只能默认从文件头计算偏移量,即whence参数为1或2时,offset参数只能取0,Python解释器不接受非零偏移量。当文件以二进制方式打开时,可以使用上述参数值进行定位。 【例57】用seek()函数在指定位置写文件。 exampleFile=open("d:\\python\\example.txt","w") exampleFile.write("0123456789") exampleFile.seek(3) exampleFile.write("ZUT") exampleFile.close() exampleFile=open("d:\\python\\example.txt") s=exampleFile.read() print(s) exampleFile.close() 运行结果是: '012ZUT6789' 注意: 在追加模式"a"下打开文件,不能使用seek()函数进行定位追加。改用"a+"模式打开文件,即可使用seek()函数进行定位。 5.2.5文件的关闭 应该牢记使用close()方法关闭文件。关闭文件是取消程序和文件之间连接的过程,内存缓冲区的所有内容将写入磁盘,因此必须在使用文件后关闭文件以确保信息不会丢失。 要确保文件关闭,可以使用try/finally语句,在finally子句中调用close()方法: helloFile=open("d:\\python\\hello.txt","w") try : helloFile.write("Hello,Sunny Day!") finally: helloFile.close() 也可以使用with语句自动关闭文件: with open("d:\\python\\hello.txt") as helloFile: s=helloFile.read() print(s) with语句可以打开文件并赋值给文件对象,之后就可以对文件进行操作。文件会在语句结束后自动关闭,即使是由于异常引起的结束也是如此。 5.2.6二进制文件的读/写 Python没有二进制类型,但是可以用string(字符串)类型来存储二进制类型数据,因为string是以字节为单位的。 1. 数据转换成字节串 pack()方法可以把数据转换成字节串(以字节为单位的字符串)。 格式: pack(格式化字符串,数据) 格式化字符串中可用的格式字符见表52中的格式字符。例如: import struct a=20 bytes=struct.pack('i',a)#将a变为string字符串 print(bytes) 运行结果是: b'\x14\x00\x00\x00' 此时bytes就是一个字符串,字符串按字节同a的二进制存储内容相同。结果中\x是十六进制的意思,20的十六进制是14。 如果字符串是由多个数据构成的,代码如下: a='hello' b='world!' c=2 d=45.123 bytes=struct.pack('5s6sif',a.encode('utf8'),b.encode('utf8'),c,d) '5s6sif'就是格式化字符串,由数字加字符构成。5s表示占5个字符宽度的字符串,2i表示2个整数等。表52是可用的格式字符及对应C语言、Python中的类型。 表52可用的格式字符及对应C语言、Python中的类型 格式字符C语言中的类型Python中的类型字节数 ccharstring of length 11 bsigned charinteger1 Bunsigned charinteger1 ?_boolbool1 hshortinteger2 Hunsigned shortinteger2 iintinteger4 Iunsigned intinteger or long4 llonginteger4 Lunsigned longlong4 续表 格式字符C语言中的类型Python中的类型字节数 qlong longlong8 Qunsigned long longlong8 ffloatfloat4 ddoublefloat8 schar[]string1 pchar[]string1 Pvoid *long与OS有关 bytes=struct.pack('5s6sif',a.encode('utf8'),b.encode('utf8'),c,d) 此时的bytes就是二进制形式的数据了,可以直接写入文件。例如: binfile=open("d:\\python\\hellobin.txt","wb") binfile.write(bytes) binfile.close() 2. 字节串还原成数据 unpack()方法可以把相应数据的字节串还原成数据。 bytes=struct.pack('i',20)#将20变为字节串 再进行反操作,将现有的字节串(其实就是二进制数据bytes)转换成Python的数据类型: a,=struct.unpack('i',bytes) 注意: unpack返回的是元组,所以如果只有一个变量: bytes=struct.pack('i',a) 那么解码的时候需要: a,=struct.unpack('i',bytes) 或者 (a,)=struct.unpack('i',bytes) 如果直接用a=struct.unpack('i',bytes),那么a=(20,)是一个元组而不是原来的整数。 例如,把"d:\\python\\hellobin.txt"文件中的数据读取并显示: import struct binfile=open("d:\\python\\hellobin.txt","rb") bytes=binfile.read() (a,b,c,d)=struct.unpack('5s6sif',bytes)#通过struct.unpack()解码成Python变量 t=struct.unpack('5s6sif',bytes)#通过struct.unpack()解码成元组 print(t) 读取结果是: (b'hello', b'world!', 2, 45.12300109863281) 5.3文件夹的操作 视频讲解 文件有两个关键属性: 路径和文件名。路径指明了文件在磁盘上的位置。例如,Python安装在路径D:\Python35下,在这个文件夹下可以找到python.exe文件,运行该文件可以打开Python的交互界面。文件名圆点的后面部分称为扩展名(或后缀),它指明了文件的类型。 路径中的D:\称为根文件夹,它包含了本分区内所有其他文件和文件夹。文件夹可以包含文件和其他子文件夹。Python35是D盘下的一个子文件夹,它包含了python.exe文件。 5.3.1当前工作目录 每个运行在计算机上的程序,都有一个当前工作目录。所有没有从根文件夹开始的文件名或路径,都假定工作在当前工作目录下。在交互式环境中输入以下代码: import os os.getcwd() 运行结果为: 'D:\\Python35' 在Python的GUI环境中运行时,当前工作目录是D:\Python35。路径中多出的一个反斜杠是Python的转义字符。 5.3.2目录操作 在大多数操作系统中,文件被存储在多级目录(文件夹)中。这些文件和目录(文件夹)被称为文件系统。Python的标准os模块可以处理它们。 1. 创建新目录 程序可以用os.makedirs()函数创建新目录。在交互式环境中输入以下代码: import os os.makedirs("e:\\python1\\ch5files") os.makedirs()在E盘下分别创建了python1文件夹及其子文件夹ch5files,也就是说,路径中所有必需的文件夹都会被创建。 2. 删除目录 当目录不再使用,可以将它删除。使用rmdir()函数删除目录: import os os.rmdir("e:\\python1") 这时出现错误: WindowsError: [Error 145] : 'e:\\python1' 因为rmdir()函数删除文件夹时要保证文件夹内不包含文件及子文件夹。也就是说,os.rmdir()函数只能删除空文件夹。 os.rmdir("e:\\python1\\ch5files") os.rmdir("e:\\python1") os.path.exists("e:\\python1")#运行结果为False Python的os.path模块包含了许多与文件名及文件路径相关的函数。上面的例子使用了os.path.exists()函数判断文件夹是否存在。os.path是os模块中的模块,所以只要执行import os就可以导入它。 3. 列出目录内容 使用os.listdir()函数可以返回给出路径中文件名及文件夹名的字符串列表: os.mkdir("e:\\python1") os.listdir("e:\\python1") [] os.mkdir("e:\\python1\\ch5files") os.listdir("e:\\python1") ['ch5files'] dataFile=open("e:\\python1\\ data1.txt","w") for n in range(26): dataFile.write(chr(n+65)) dataFile.close() os.listdir("e:\\python1") ['ch5files', 'data1.txt'] 刚创建的python1文件夹是一个空文件夹,所以返回的是一个空列表。后续在文件夹下分别创建了一个子文件夹ch5files和一个文件data1.txt,列表里返回的是子文件夹名和文件名。 4. 修改当前目录 使用os.chdir()函数可以更改当前工作目录: os.chdir("e:\\python1") os.listdir(".")#.代表当前工作目录 ['ch5files', 'data1.txt'] 5. 查找匹配文件或文件夹 使用glob()函数可以查找匹配文件或文件夹(目录)。glob()函数使用UNIX Shell的规则来查找。 *: 匹配任意一个任意字符。 ?: 匹配单个任意字符。 [字符列表]: 匹配字符列表中的任意一个字符。 [!字符列表]: 匹配除列表外的其他字符。 import glob glob.glob("d*")#查找以d开头的文件或文件夹 glob.glob("d????") #查找以d开头并且全长为5个字符的文件或文件夹 glob.glob("[abcd]*") #查找以abcd中任一字符开头的文件或文件夹 glob.glob("[!abd]*") #查找不以abd中任一字符开头的文件或文件夹 5.3.3文件操作 os.path模块主要用于文件的属性获取,在编程中经常用到。 1. 获取路径和文件名 os.path.dirname(path): 返回path参数中的路径名称字符串。 os.path.basename(path): 返回path参数中的文件名。 os.path.split(path): 返回参数的路径名称和文件名组成的字符串元组。 helloFilePath="e:\\python\\ch5files\\hello.txt" os.path.dirname(helloFilePath) 'e:\\python\\ch5files' os.path.basename(helloFilePath) 'hello.txt' os.path.split(helloFilePath) ('e:\\python\\ch5files', 'hello.txt') helloFilePath.split(os.path.sep) ['e:', 'python', 'ch5files', 'hello.txt'] 如果想要得到路径中每一个文件夹的名字,可以使用字符串方法split,通过os.path.sep对路径进行正确的分隔。 2. 检查路径有效性 如果提供的路径不存在,许多Python函数就会崩溃报错。os.path模块提供了一些函数帮助判断路径是否存在。 os.path.exists(path): 判断参数path的文件或文件夹是否存在。是则返回True,否则返回False。 os.path.isfile(path): 判断参数path若存在且是一个文件,则返回True,否则返回False。 os.path.isdir(path): 判断参数path若存在且是一个文件夹,则返回True,否则返回False。 3. 查看文件大小 os.path模块中的os.path.getsize()函数可以查看文件大小。此函数与前面介绍的os.path.listdir()函数配合可以帮助统计文件夹大小。 【例58】统计d:\\python文件夹下所有文件的大小。 import os totalSize=0 os.chdir("d:\\python") for fileName in os.listdir(os.getcwd()): totalSize+=os.path.getsize(fileName) print( totalSize) 4. 重命名文件 os.rename()函数可以帮助重命名文件。 os.rename("d:\\python\\hello.txt","d:\\python\\helloworld.txt") 5. 复制文件和文件夹 shutil模块中提供一些函数,可以帮助复制、移动、改名和删除文件夹,还可以实现文件的备份。 shutil.copy(source,destination): 复制文件。 shutil.copytree(source,destination): 复制整个文件夹,包括其中的文件及子文件夹。 例如,将e:\\python文件夹复制为新的e:\\pythonbackup文件夹,代码如下: import shutil shutil.copytree("e:\\python","e:\\python-backup") for fileName in os.listdir("e:\\python-backup"): print (fileName) 使用这些函数前先导入shutil模块。shutil.copytree()函数复制包括子文件夹在内的所有文件夹内容。 shutil.copy("e:\\python1\\data1.txt","e:\\python-backup") shutil.copy("e:\\python1\\data1.txt","e:\\python-backup\\data-backup.txt") shutil.copy()函数的第二个参数destination可以是文件夹,表示将文件复制到新文件夹里; 也可以是包含新文件名的路径,表示复制的同时将文件重命名。 6. 文件和文件夹的移动和改名 shutil.move(source,destination): 与shutil.copy()函数用法相似,参数destination既可以是一个包含新文件名的路径,也可以仅包含文件夹。 shutil.move("e:\\python1\\data1.txt","e:\\python1\\ch5files") shutil.move("e:\\python1\\data1.txt","e:\\python1\\ch5files\\data2.txt") 注意: 不管是shutil.copy()函数还是shutil.move()函数,函数参数中的路径必须存在,否则Python会报错。 如果参数destination中指定的新文件名与文件夹中已有文件重名,则文件夹中的已有文件会被覆盖。因此,使用shutil.move()函数应当小心。 7. 删除文件和文件夹 os模块和shutil模块都有函数可以删除文件或文件夹。 os.remove(path)/os.unlink(path): 删除参数path指定的文件。 os.remove("e:\\python-backup\\data-backup.txt") os.path.exists("e:\\python-backup\\data-backup.txt")#False os.rmdir(path): 如前所述,os.rmdir()函数只能删除空文件夹。 shutil.rmtree(path): 删除整个文件夹,包含所有文件及子文件夹。 shutil.rmtree("e:\\python1") os.path.exists("e:\\python1")#False 这些函数都是从硬盘中彻底删除文件或文件夹,不可恢复,因此使用时应特别谨慎。 8. 遍历目录树 想要处理文件夹中包括子文件夹内的所有文件即遍历目录树,可以使用os.walk()函数。os.walk()函数将返回该路径下所有文件及子目录信息元组。 【例59】显示 H:\\档案科技表格文件夹下所有文件及子目录。 import os list_dirs = os.walk("H:\\档案科技表格") #返回一个元组 print(list(list_dirs)) for folderName,subFolders,fileNames in list_dirs: print("当前目录: " + folderName) for subFolder in subFolders: print(folderName +"的子目录" + " 是--" + subFolder) for fileName in fileNames: print(subFolder +"的文件 " + " 是--" + fileName) 5.4文件应用案例——游戏地图存储 视频讲解 在游戏开发中往往需要存储不同关卡的游戏(例如推箱子、连连看等游戏)的地图信息。这里以推箱子游戏地图存储为例来说明游戏地图信息如何存储到文件中并读取出来。 图54所示的推箱子游戏,可以看成7×7的表格,这样如果按行存储到文件中,就可以把这一关游戏地图存入到文件中了。 图54推箱子游戏 为了表示方便,每个格子状态值分别用常量Wall(0)代表墙,Worker(1)代表人,Box(2)代表箱子,Passageway(3)代表路,Destination(4)代表目的地,WorkerInDest(5)代表人在目的地,RedBox(6)代表放到目的地的箱子。文件中存储的原始地图中格子的状态值采用相应的整数形式存放。假如推箱子游戏界面的对应数据如下所示。 0003300 3303400 1332330 4203330 3330330 3330030 3000000 5.4.1地图写入文件 只需要使用write()方法按行/列(这里按行)存入文件map1.txt中即可。 import os #地图写入文件 (helloFile=open("map1.txt","w") helloFile.write("0,0,0,3,3,0,0\n") helloFile.write("3,3,0,3,4,0,0\n") helloFile.write("1,3,3,2,3,3,0\n") helloFile.write("4,2,0,3,3,3,0\n") helloFile.write("3,3,3,0,3,3,0\n") helloFile.write("3,3,3,0,0,3,0\n") helloFile.write("3,0,0,0,0,0,0\n") helloFile.close() 5.4.2从地图文件读取信息 只需要按行从文件map1.txt中读取即可得到地图信息。本例中将信息读取到二维列表中存储。 #读文件 helloFile=open("map1.txt","r") myArray1=[] while True: line=helloFile.readline() if line=="":#或者 if not line break line=line.replace("\n","")#将读取的1行中最后的换行符去掉 myArray1.append(line.split(",")) helloFile.close() print(myArray1) 运行结果是: [['0', '0', '0', '3', '3', '0', '0'], ['3', '3', '0', '3', '4', '0', '0'], ['1', '3', '3', '2', '3', '3', '0'], ['4', '2', '0', '3', '3', '3', '0'], ['3', '3', '3', '0', '3', '3', '0'], ['3', '3', '3', '0', '0', '3', '0'],['3','0','0','0',',0',',0',',0']] 在后面图形化推箱子游戏中,根据数字代号用对应图形显示到界面上,即可完成地图读取任务。 5.5文件应用案例——词频统计 对文章内容进行统计,从中找出出现频率高的词语,从而概要分析文章内容,是经常遇到的需求; 对网络信息进行自动检索及归档,也是同样的需求。这就是“词频统计”问题。 以英文文章为例,将文章作为文件读取其内容,对文章中的每一个单词设计其计数器,每出现一次其计数器进行加1操作,最后得出每个单词出现的次数。这里可以使用字典类型,以单词作为键,其次数为值,形成(单词,次数)键值对。而英文文章以空格或标点符号进行单词的分隔,因此获得单词并统计数量相对容易。下面对程序进行分析。 词频统计问题的IPO描述如下: (1) 程序输入: 从文件中读取文章。 (2) 处理: 使用字典类型,分词并统计每一个单词出现的次数。 (3) 程序输出: 显示统计的结果,每一个单词及其出现的次数。 (4) 程序输入: 选取李某给刚上大学女儿的一封英文信并保存在letter.txt文件中。 将文件内容读取并保存在字符串中,首先需要分词。这里先使用string.lower()函数将所有单词转为小写形式,保证同一个单词不同大小写形式统计的一致; 然后用string.replace()方法将特殊字符统一替换为空格,为后面的分词做准备,提取单词。英文文章分词比较简单,由于单词间有空格,所以string.split()按空格分隔就可以实现文章分隔成单词的列表words。 使用字典类型wdCountDict进行单词的计数。对于已经出现在字典中的单词,其计数器加1; 没有出现的单词添加并将键值设置为1,新建键值对。对应的代码如下: for word in words: if word in wdCountDict: wdCountDict[word]=wdCountDict[word]+1 else wdCountDict[word]=1 也可以使用wdCountDict.get()方法将上述代码中的if语句替换为: wdCountDict[word]=wdCountDict.get(word,0)+1 将词频结果从大到小倒序排序并输出。字典类型是无序的,因此必须先将字典转换为列表,对列表进行排序。为了使用sort()方法排序,转换列表时需要对每个字典项(键,值)转换为新元组(值,键)添加到列表中。这是因为sort()方法对复合对象,比较的是每个元素的第一个值。代码如下: valKeyList=[] for key,val in wdCountDict.items(): valKeyList.append((val,key)) 也可以使用以下代码进行更简洁的替换: valKeyList=[(val,key) for key,val in wdCountDict.items()] 全部代码如下: #letter.py def getFileText(): with open("C:\\lynn\\Python\\letter.txt","r") as letterFile: filTxt=letterFile.read() filTxt=filTxt.lower() for ch in '!"#$%&()*+-*/,.:;<=>?@[]\\^_{}|~': filTxt=filTxt.replace(ch," ") return filTxt letterTxt=getFileText() words=letterTxt.split() wdCountDict={} for word in words: wdCountDict[word]=wdCountDict.get(word,0)+1 valKeyList=[(val,key) for key,val in wdCountDict.items()] valKeyList.sort(reverse=True) print("{0:<10}{1:>5}".format("word","count")) print("*"*21) for val,key in valKeyList: print("{0:<10}{1:>5}".format(key,val)) 注意: 使用sort()方法排序时,若第一个值相同,它会使用复合对象的其他元素排序。因此,出现次数相同的单词以单词字母倒序输出。 观察结果可以发现,在列表中会出现很多常见且对文章分析无意义的词,如and、you、or、it等,这些词被称作停用词。停用词表可以在网上找到,通常可以设置停用词列表,并将它们从字典中排除。代码如下: excludes={"the","of","you","your","that","will","this","don't"} for word in excludes: del(wdCountDict[word]) 这个示例中列表中的单词并不完整,读者可以试着完善列表。 更简单的方法是排除长度小于3的单词,并在最终的结果中将出现次数小于2次的单词也排除,完善输出结果。完整代码如下: #letter.py def getFileText(): with open("C:\\lynn\\Python\\letter.txt","r") as letterFile: filTxt=letterFile.read() filTxt=filTxt.lower() for ch in '!"#$%&()*+-*/,.:;<=>?@[]\\^_{}|~': filTxt=filTxt.replace(ch," ") return filTxt letterTxt=getFileText() words=letterTxt.split() #实现文章分隔成单词的列表words wdCountDict={} excludes={"the","of","you","your","that","will","this","don't"} for word in words: wdCountDict[word]=wdCountDict.get(word,0)+1 for word in excludes: del(wdCountDict[word]) items=list(wdCountDict.items())#将字典转换为列表 items.sort(key=lambda x:x[1],reverse=True)#按记录第2列排序 print("{0:<10}{1:>5}".format("word","count")) print("*"*21) for key,val in items: if len(key)>3 and val>2: print("{0:<10}{1:>5}".format(key,val)) 此后,在最终的结果中就可以看到单词出现的次数。 5.6习题 1. 编写程序,打开任意的文本文件,读出其中内容,判断该文件中某些给定关键字(如“中国”)出现的次数。 2. 编写程序,打开任意的文本文件,在指定的位置产生一个相同文件的副本,即实现文件的复制功能。 3. 用Windows的记事本创建一个文本文件,其中每行包含一段英文。试读出文件的全部内容,并判断: (1) 该文本文件共有多少行? (2) 文件中以大写字母P开头的有多少行? (3) 一行中包含字符最多的和包含字符最少的分别在第几行? 4. 统计某test.txt文件中大写字母、小写字母和数字出现的次数。 5. 编写程序统计调查问卷各评语出现的次数,将最终统计结果放入字典。 调查问卷结果: 不满意,一般,满意,一般,很满意,满意,一般,一般,不满意,满意,满意,满意,满意,一般,很满意,一般,满意,不满意,一般,不满意,满意,满意,满意,满意,满意,满意,很满意,不满意,满意,不满意,不满意,一般,很满意 要求: 问卷调查结果用文本文件result.txt保存并编写程序读取该文件后统计各评语出现的次数,将字典最终统计结果追加至result.txt文件中。 6. 文件src.txt存储的是一篇英文文章,将其中所有大写字母转换成小写字母输出。 假如src.txt里面存储内容为: This is a Book 则输出内容应为: this is a book 7. 文件“score.txt”中存储了歌手大奖赛中10名评委给每一个歌手打的分,10个分数在一行,形式如下: 歌手1,8.92,7.89,8.23,8.93,7.89,8.52,7.99,8.83,8.99,8.89 歌手2,8.95,8.86,8.24,8.63,7.66,8.53,8.59,8.82,8.93,8.89 …… 从文件中读取数据,存入列表中,计算该名歌手的最终得分,最终得分的计算方式是10个评分去掉最高分,去掉最低分,然后求平均分。最终得分保留两位小数,输出到屏幕。