第5章〓Python文件的使用 在程序运行时,数据保存在内存的变量里。内存中的数据在程序结束或关机后就会消失。如果想要在下次开机运行程序时还 使用同样的数据,就需要把数据存储在不易失的存储介质中,如硬盘、光盘或U盘。不易失存储介质上的数据保存在以存储路径命名的文件中。通过读/写文件,程序就可以在运行时保存数据。在本章中,要学习使用Python在磁盘上创建、读写以及关闭文件。本章只讲述基本的文件操作函数,更多函数请参考Python标准文档。 视频讲解 5.1文件 简单地说,文件是由字节组成的信息,在逻辑上具有完整意义,通常在磁盘上永久保存。Windows系统的数据文件按照编码方式分为两大类: 文本文件和二进制文件。文本文件可以处理各种语言所需的字符,只包含基本文本字符,不包括诸如字体、字号、颜色等信息。它可以在文本编辑器和浏览器中显示,即在任何情况下,文本文件都是可读的。 使用其他编码方式的文件即二进制文件,如Word文档、PDF、图像和可执行程序等。如果用文本编辑器打开一个JPG文件或Word文档,会看到一堆乱码,如图51所示。也就是说,每种二进制文件都需要自己的处理程序才能打开并操作。 图51文本编辑器Notepad打开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文件。“读模式”是Python打开文件的默认模式。当文件以读模式打开时,只能从文件中读取数据而不能向文件写入或修改数据。 当调用open()函数时将返回一个文件对象,在本例中文件对象保存在helloFile变量中。 >>> print(helloFile) <_io.TextIOWrapper name='d:\\python\\hello.txt' mode='r' encoding='cp936'> 打印文件对象时可以看到文件名、读/写模式和编码格式。cp936就是指Windows系统里第936号编码格式,即GB2312的编码。接下来就可以调用helloFile文件对象的方法读取文件中的数据了。 5.2.2读取文本文件 可以调用文件file对象的多种方法读取文件内容。 1. read()方法 不设置参数的read()方法将整个文件的内容读取为一个字符串。read()方法一次读取文件的全部内容,性能根据文件大小而变化,如1GB的文件读取时需要使用同样大小的内存。 【例51】调用read()方法读取hello文件中的内容。 helloFile=open("d:\\python\\hello.txt") fileContent=helloFile.read() helloFile.close() print(fileContent) 输出结果: Hello! HenanZhengzhou 也可以设置最大读入字符数来限制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文件的内容。 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写文本文件 写文件与读文件相似,都需要先创建文件对象连接。所不同的是,打开文件时是以“写”模式或“添加”模式打开。如果文件不存在,则创建该文件。 与读文件时不能添加或修改数据类似,写文件时也不允许读取数据。“w”写模式打开已有文件时,会覆盖文件原有内容,从头开始,就像用一个新值覆写一个变量的值。 例如: >>> helloFile=open("d:\\python\\hello.txt","w")#"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() 由于“w”写模式打开已有文件,文件原有内容会被清空,所以再次读取内容时长度为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所示。许多方法隐式使用当前位置。例如,调用readline()方法后,文件当前位置移动到下一个回车处。 图53文件当前位置 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() 4 >>> exampleFile.close() 这里exampleFile.tell()函数返回的是一个整数4,表示文件当前位置和开始位置之间有4B偏移量。因为已经从文件中读取4个字符了,所以有4B的偏移量。 seek()函数设置新的文件当前位置,允许在文件中跳转,实现对文件的随机访问。 seek()函数有两个参数,第一个参数是字节数,第二个参数是引用点。seek()函数将文件当前指针由引用点移动指定的字节数到指定的位置。语法如下。 seek(offset[,whence]) 说明: offset是一个字节数,表示偏移量。引用点whence有以下三个取值。 文件开始处为0,也是默认取值。意味着使用该文件的开始处作为基准位置,此时字节偏移量必须非负。 当前文件位置为1,则是使用当前位置作为基准位置。此时偏移量可以取负值。 文件结尾处为2,则该文件的末尾将被作为基准位置。 【例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' 注意,在追加模式下打开文件,不能使用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.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.move()函数与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()函数删除整个文件夹,包含所有文件及子文件夹。 shutil.rmtree("e:\\python1") os.path.exists("e:\\python1")#False 这些函数都是从硬盘中彻底删除文件或文件夹,不可恢复,因此使用时应特别谨慎。 8. 遍历目录树 想要处理文件夹中包括子文件夹内的所有文件即遍历目录树,可以使用os.walk()函数。os.walk()函数将返回该路径下所有文件及子目录信息元组。 【例59】显示“d:\档案科技表格”文件夹下所有文件及子目录。 import os list_dirs = os.walk("d:\档案科技表格")#返回一个元组 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常用格式文件操作 5.4.1操作CSV格式文件 CSV(CommaSeparated Values,逗号分隔值)文件以纯文本形式存储表格数据。CSV文件由任意数量的记录组成,记录间以换行符分隔; 每条记录由字段组成,字段间的分隔符常见的是逗号或制表符。例如,CSV格式文件 test2.CSV: 序号,姓名,年龄 3,张海峰,25 4,伟,38 5,赵大强,36 … 1. 直接读写CSV文件 CSV文件是一种特殊的文本文件,且格式简单,可以根据文本文件的读写方法实现操作CSV文件。 【例510】直接读取CSV格式文件到二维列表。 myfile=open('test2.csv', 'r') ls=[] for line in myfile: line=line.replace('\n','')#换行符去掉 ls.append(line.split(','))#将一行中以','分隔的多个数据转换成数据元素的列表 print(ls) myfile.close()#close 文件 以上代码将一行转换成一个列表,多行数据就组成一个每个元素都是列表的列表,即二维列表。 运行结果类似如下。 [['序号', '姓名', '年龄'], ['3', '张海峰', '25'], ['4', '李伟', '38'], ['5', '赵大强', '36']] 【例511】直接将二维列表写入CSV格式文件。 myfile=open('test2new.csv', 'w')#新建CSV文件并以写模式打开 ls=[['序号', '姓名', '年龄'], ['3', '张海峰', '25'], ['4', '李伟', '38'], ['5', '赵大强', '36'], ['6', '程海鹏', '28']] for line in ls: myfile.write(','.join(line)+"\n")#在同一行的数据元素之间加上逗号间隔 myfile.close() 在写入过程中,与读取数据时的处理相反,需要借助字符串的join()方法,在同一行的数据元素之间加上逗号间隔。同时行尾加上换行符"\n"。 2. 使用csv模块读写CSV文件 Python自带的csv模块可以处理CSV文件,与读写Excel文件相比,CSV文件的读写是相当方便的。 读取CSV文件使用reader对象,格式如下。 reader(csvfile[, dialect='excel'][, fmtparam]) csvfile: 通常的文件(file)对象或者列表(list)对象都是适用的。 dialect: 编码风格,默认为Excel方式,也就是逗号(,)分隔。另外,csv模块也支持exceltab风格,也就是制表符(tab)分隔。 fmtparam: 格式化参数,用来覆盖之前dialect对象指定的编码风格。 reader对象是可以迭代的,line_num属性表示当前行数; reader对象还提供一些dialect、next()方法。 写入CSV文件使用writer对象,格式如下。 writer(csvfile, dialect='excel', fmtparams) 参数的意义同上,不再赘述。这个对象有两个函数writerow()和writerows()实现写入CSV文件。例如: with open('1.csv','w',newline='') as f: head = ['标题列1','标题列2'] rows = [ ['张三',80],['李四',90] ] writer = csv.writer(f) writer.writerow(head)#写入一行数据 writer.writerows(rows)#写入多行数据 【例512】将人员信息写入CSV文件并读取出来。 import csv #写入一个文件 myfile = open('test2.csv', 'w', newline='')#'a'追加,'w'写方式 mywriter = csv.writer(myfile)#返回一个Writer对象 mywriter.writerow(['序号', '姓名', '年龄'])#加入标题行 mywriter.writerow([3, '张海峰', 25])#加入一行 mywriter.writerow([4, '李伟', 38]) mywriter.writerows([[5, '赵大强', 36],[6, '程海鹏', 28]])#加入多行 myfile.close() #读取一个 CSV 文件 myfilepath = 'test2.csv' #这里用到的 open()都要加上 newline='',否则会多一个换行符(参见标准库文档) myfile = open(myfilepath, 'r', newline='') myreader = csv.reader(myfile)#返回一个reader对象 for row in myreader: if myreader.line_num == 1 :#line_num是从 1 开始计数的 continue#第一行不输出 for i in row :#row是一个列表 print(i, end=' ') print() myfile.close()#close 文件 这个程序涉及下面的函数: writer.writerow(list)是将list列表以一行形式添加; writer.writerows(list)可写入多行。 程序运行结果如下。 3 张海峰 25 4 李伟 38 5 赵大强 36 6 程海鹏 28 并生成与显示同样内容的test2.csv文件,不过还有序号、姓名、年龄这样的标题行信息。 5.4.2操作Excel文档 Excel是电子表格,包含文本、数值、公式和格式。第三方的xlrd和xlwt两个模块分别用来读和写Excel,只支持.xls和.xlsx格式,Python不默认包含这两个模块。这两个模块之间相互独立,没有依赖关系,也就是说,可以根据需要只安装其中一个。xlrd和xlwt模块安装可以在命令行下使用pip install <模块名>: pip install xlrd pip install xlwt 当看到类似Successfully的字样时,表明已经安装成功了。 1. 使用xlrd模块读取Excel xlrd提供的接口比较多,常用的如下。 open_workbook()打开指定的Excel文件,返回一个Book工作簿对象。 data = xlrd.open_workbook('excelFile.xls')#打开Excel文件 1) Book工作簿对象 通过Book工作簿对象可以得到各个Sheet工作表对象(一个Excel文件可以有多个Sheet,每个Sheet就是一张表格)。Book工作簿对象的属性和方法如下。 Book.nsheets返回Sheet的数目。 Book.sheets()返回所有Sheet对象的list。 Book.sheet_by_index(index)返回指定索引处的Sheet,相当于Book.sheets()[index]。 Book.sheet_names()返回所有Sheet对象名字的list。 Book.sheet_by_name(name)根据指定Sheet对象名字返回Sheet。 例如: table = data.sheets()[0]#通过索引获取 Sheet table = data.sheet_by_index(0)#通过索引获取 Sheet table = data.sheet_by_name('Sheet1')#通过名称获取Sheet 2) Sheet工作表对象 通过Sheet对象可以获取各个单元格,每个单元格是一个Cell对象。Sheet对象的属性和方法如下。 Sheet.name返回表格的名称。 Sheet.nrows返回表格的行数。 Sheet.ncols返回表格的列数。 Sheet.row(r)获取指定行,返回Cell对象的list。 Sheet.row_values(r)获取指定行的值,返回list。 Sheet.col(c)获取指定列,返回Cell对象的list。 Sheet.col_values(c)获取指定列的值,返回list。 Sheet.cell(r, c)根据位置获取Cell对象。 Sheet.cell_value(r, c)根据位置获取Cell对象的值。例如: cell_A1 = table.cell(0,0).value#获取A1单元格的值 cell_C4 = table.cell(2,3).value#获取C4单元格的值 例如,循环输出表数据: nrows = table.nrows#表格的行数 ncols = table.ncols#表格的列数 for i in range(nrows): print(table.row_values(i)) 3) Cell对象 Cell对象的Cell.value返回单元格的值。 【例513】读取如图54所示的Excel文件 test.xls示例。 import xlrd wb = xlrd.open_workbook('test.xls')#打开文件 sheetNames = wb.sheet_names()#查看包含的工作表 print(sheetNames)#输出所有工作表的名称,['sheet_test'] #获得工作表的两种方法 sh = wb.sheet_by_index(0) sh = wb.sheet_by_name('sheet_test')#通过名称'sheet_test'获取对应的Sheet #单元格的值 cellA1 = sh.cell(0,0) cellA1Value = cellA1.value print(cellA1Value)#王海 #第一列的值 columnValueList = sh.col_values(0) print(columnValueList)#['王海', '程海鹏'] 程序运行结果如下。 ['sheet_test'] 王海 ['王海', '程海鹏'] 2. 使用xlwt模块写Excel 相对来说,xlwt提供的接口就没有xlrd那么多了,主要有以下几个。 Workbook()是构造函数,返回一个工作簿的对象。 Workbook.add_sheet(name)添加了一个名为name的表,类型为Worksheet。 Workbook.get_sheet(index)可以根据索引返回Worksheet。 Worksheet.write(r, c, vlaue)是将vlaue填充到指定位置。 Worksheet.row(n)返回指定的行。 Row.write(c, value)在某一行的指定列写入value。 Worksheet.col(n)返回指定的列。 通过对Row.height或Column.width赋值可以改变行或列默认的高度或宽度(单位: 0.05pt,即1/20pt)。 Workbook.save(filename)保存文件。 表的单元格默认是不可重复写的,如果有需要,在调用add_sheet()的时候指定参数cell_overwrite_ok=True即可。 【例514】写入Excel示例代码。 import xlwt book = xlwt.Workbook(encoding='utf-8') sheet = book.add_sheet('sheet_test', cell_overwrite_ok=True)#单元格可重复写 sheet.write(0, 0, '王海') sheet.row(0).write(1, '男') sheet.write(0, 2, 23) sheet.write(1, 0, '程海鹏') sheet.row(1).write(1, '男') sheet.write(1, 2, 41) sheet.col(2).width = 4000#单位1/20 pt book.save('test.xls') 程序运行生成如图54所示test.xls文件。 图54test.xls文件 5.4.3操作JSON格式文件 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,比XML更小、更快、更易解析,易于读写且占用带宽小,网络传输速度快,适用于数据量大、不要求保留原有类型的情况。它是JavaScript的子集,易于人阅读和编写。 前端和后端进行数据交互,其实往往就是通过JSON进行的。因为JSON易于被识别的特性,常被作为网络请求的返回数据格式。在爬取动态网页时,会经常遇到JSON格式的数据,Python中可以使用json模块来对JSON数据进行解析。 1. JSON的结构 常见形式为“名称/值”对的集合。 例如: {"firstName": "Brett", "lastName": "McLaughlin"} JSON允许使用数组,采用方括号[]实现。 例如,用JSON表示中国部分省市数据如下,其中省份采用的是数组。 { "name": "中国", "province": [{ "name": "黑龙江", "cities": { "city": ["哈尔滨", "大庆"] } }, { "name": "广东", "cities": { "city": ["广州", "深圳", "珠海"] } } ] } 2. json模块中常用的方法 在使用json这个模块前,首先要导入json库: import json。 它主要提供了4个方法dumps、dump、loads、load,如表52所示。 表52json模块中常用的方法 方法功 能 描 述 json.dumps()将 Python 对象转换成JSON字符串 json.loads()将JSON字符串转换成Python对象 json.dump()将Python类型数据序列化为JSON对象后写入文件 json.load()读取文件中JSON形式的字符串并转换为Python类型数据 下面通过例子说明这4个方法的使用。 1) json.dumps() 其作用是将Python对象转换成JSON字符串。 import json data = {'name':'nanbei','age':18} s= json.dumps(data)#将Python对象编码成JSON字符串 print(s) 运行结果: {"name": "nanbei", "age": 18} JSON注意事项: 名称必须用双引号(即"name")来包括。 值可以是字符串、数字、true、false、null、数组或子对象。 从运行结果可见,原先的'name','age'单引号已经变成双引号"name", "age"。 2) json.loads() 其作用是将JSON字符串转换成Python对象。 import json data = "{'name':'nanbei','age':18}" a = json.dumps(data) print(json.loads(a))#将JSON字符串编码成Python对象——dict字典 运行结果: {'name': 'nanbei', 'age': 18} 如果是一个JSON文件则要先读文件,然后才能转换成Python对象。 import json f=open('stus.json',encoding='utf-8')#'stus.json'是一个JSON文件 content=f.read()#使用loads()方法,需要先读文件成字符串 user_dic=json.loads(content)#转换成Python的字典对象 print(user_dic) 3) json.load()方法 该方法的作用是读取文件中JSON形式的字符串并转换为Python类型数据。 import json f=open('stus.json',encoding='utf-8') user_dic=json.load(f)#f是文件对象 print(user_dic) 可见,loads()传入的是字符串,而load()传入的是文件对象。使用loads()时需要先读文件成字符串再使用,而load()则不用先读文件成字符串而是直接传入文件对象。 4) json.dump() 该方法的作用是将Python类型数据序列转换为JSON对象后写入文件。 stus={ 'xiaojun':88,'xiaohei':90,'lrx':100} f=open('stus2.json','w',encoding='utf-8')#以写方式打开stus2.json文件 json.dump(stus,f)#写入stus2.json文件 f.close()#文件关闭 实验五文件操作 一、 实验目的 通过本实验,掌握Python语言文件的读写方法以及打开和关闭等基本操作,理解并应用文件操作相关知识。 二、 实验要求 (1) 掌握文件的打开(或建立)和关闭方法。 (2) 掌握文件的读写方法。 (3) 掌握文件和文件夹(目录)的操作方法。 三、 实验内容与步骤 (1) 编程实现文件读写操作,完成以下功能。 ① 随机生成100个100~500的整数,每10个整数占一行,写入test.txt文件。 import random f=open("test.txt", 'w') for i in range(1,101): if i%10==0: f.write(str(random.randrange(100,500))+'\n') else: f.write(str(random.randrange(100,500))+' ') f.close() ② 读取test.txt文件,显示每行数据并计算出每行最大值。 f2=open("test.txt", 'r') s=f2.readlines()#读取文件 f2.close() for m in s: m=m.strip() ls=m.split(' ') print(ls) print(max(int(c) for c in ls)) ③ 统计偶数的个数并写入test.txt文件中。 f3=open("test.txt", 'r+') s=f3.readlines()#读取文件 n=0 for m in s: m=m.strip() ls=m.split(' ') ls2=[int(c) for c in ls] for x in ls2: if x%2==0: n=n+1 f3.write("偶数的个数是: "+str(n)) f3.close() (2) 假设有一个英文文本文件demo.txt,要求完成如下操作。 ① 读取demo.txt文件内容并显示。 ② 编写程序对读取内容进行加密,并把加密后内容保存到另一个文件cipher.txt中。按照以下加密规则对其加密后输出其密文形式(加密规则: 字母a→z,b→y,c→x,…,x→c,y→b,z→a,其他字符保持不变)。 ③ 编程实现对密文的解密。 分析: 根据加密规则,很容易得到如下转换公式。 newc=122 - ord(c) + 97 其中,122是ord('z'),即'z'字符的ASCII码; 97是ord('a')即'a'字符的ASCII码。ord函数将字符转换为其ASCII码,chr函数则相反,即把一个ASCII码整数转换成字符。 同时可以看出加密和解密公式一致。 def inv(c):#加密和解密公式实现 if 'a' <= c <= 'z': return chr(122 - ord(c) + 97)#'z'-c+'a' if 'A' <= c <= 'Z': return chr(90 - ord(c) + 65)#'Z'-c+'A' return c def jiami(ls):#将字符串转换成加密后的字符串 ls2=[] for i in range(len(ls)): c=ls[i] if 'a'<=c<='z': ls2.append(inv(c)) else: ls2.append(c) return ''.join(ls2)#将列表列连接字符串 实际上,以上加密函数如下实现。 def jiami(ls):#将字符串转换成加密后的字符串 ls2=[inv(c) for c in ls]#列表生成式 return ''.join(ls2) 以下是主程序读取文件demo.txt内容,并把加密后的列表r写入文件cipher.txt,最后读取文件cipher.txt,采用与加密一样的函数进行解密并打印出来。 f=open('demo.txt','r') s=f.readlines()#读取文件 f.close() r=[jiami(ls) for ls in s] #写入文件cipher.txt f=open('cipher.txt','w+')#可读写模式 f.writelines(r) f.seek(0) result=f.readlines() print('转换结果为: ') for s in result: print(s.strip())#strip()法用于移除字符串头尾指定的字符(默认为空格或换行符) f.close() #解密方法和加密一致 r=[jiami(ls) for ls in result] print('原文为: ') for s in r: print(s.strip()) 运行结果如下。 转换结果为: zyxwvut cba 原文为: abcdefg xyz 四、 编程并上机调试 (1) 编写程序,将100以内所有素数写入文件result1.txt中。 (2) 编写程序,将100以内孪生素数写入文件result2.txt中。(孪生素数指两个素数相差为2,例如,3和5、5和7、11和13等都是孪生素数。) (3) 编写程序,统计一篇英文文章中单词的个数,并输出其中出现最多的前5个单词。 (4) 编写程序实现,在文件“data.txt”的第一行中写入自己的学号和姓名,然后随机生成20个[100,200]的整数,统计出最大值,并将这些整数写入文件“data.txt”中,每行一个数字,以换行符分开。 (5) 文件file.txt存储的是一篇英文文章,编写程序,将其中所有小写字母转换成大写字母输出。 习题 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名评委的分数在同一行,形式如下。 歌手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 … 从文件中读取数据并存入列表,计算歌手的最终得分,其计算方式是去掉最高分和最低分后求平均分。最终得分保留两位小数,将其输出到屏幕。