为了长期保存数据,也为了能和其他人分享或使用相关数据,就需要对文件进行操作。在实际工 程应用中,经常会出现读取某个文件中的数据并在经过某些处理后再把结果存到其他文件的情况,这 时就要用到对文件的操作了。本章提到的文件主要包括文本文件(没有特定格式的纯文本文件,如txt 文件等,内容由各行组成,行末有回车换行符,文本文件中的内容无须特定的解析软件即可直接阅读)、 csv文件等简单类型。另外,经常遇到的文件格式还有各种二进制文件(内容以字节串的形式进行存 储,如jpg 图片、mp4文档、数据库文件、PDF 文档等;通常情况下,若无专门的软件进行解码,其中的内 容是无法直接编辑、阅读的)等。由于对文件的操作需要在相应的文件夹下进行,因此掌握路径的操作 也是必要的。限于本书的科普性质,本章只学习针对txt文件、csv文件等常见文本文件的操作以及相 应的基本路径的操作方法。 学习本章内容时,要求: .掌握Python常用的文件操作方法,包括常用内置方法和部分标准方法、外部引用包的携带方 法等; .掌握txt文件、csv文件等常见文本文件的打开、读取数据、写入信息的基本操作方法; .掌握文件路径的基本操作方法。 5.1 读写文本文件 为了长期保存、传输、修改数据,很多时候需要用文件(包括数据库)存储和管理相关的数据。当程 序运行时,变量虽然可用来保存和传递数据,但变量以及对象中存储的数据是暂时的,程序结束后就会 丢失。如果希望永久保留数据,就需要将数据保存到文件中,这时就要用到文件操作了。Python提供 了内置的文件对象,以及对文件、目录进行操作的内置模块。可以说,掌握对文件及其路径的操作是十 分重要的。 文件一般可以分为纯文本文件(如txt、ini文件等)、特定的表格文件(如csv文件等), 以及非文本 131 文件(如pdf、docx、jpg、mp4等)。对文件的操作一般包括打开文件(open)、读取数据(read)、写入数据 (write)、关闭文件(close)等。对于其他非纯文本的二进制文件(如JPEG、数据库文件、PDF文档等), 需要了解二进制文件的读取方法,这里可能要用到序列化的概念,它可以理解为将内存中的数据在不 丢失其类型信息的情况下转换成对象的二进制形式的过程,Python中常用的序列化模块有struct、 pickle模块等。限于篇幅及本书的科普定位,这里不再详细介绍二进制文件的读写方式,且不再区分纯 文本文件和特定的表格文件。本章将简介txt、csv(以逗号等符号作为分隔符的特殊文本文件)等类型 的文件的读写与数据使用方法。 5.1.1 打开和关闭文件的基本操作 Python内置了不少和文件操作有关的方法,如通过open()方法可按指定的模式(mode)“读”或 “写”或“追加”数据。通过内置方法open()可以打开路径下的某个文件;如成功,则返回一个可迭代的 文件对象;如果欲打开的文件不存在或打开失败,则系统会给出异常,其语法如下(注:对中学生来说, 只需要了解file、mode、encoding参数的使用方法即可;若需要深入了解其他相关参数的使用方法,可 参考相关手册,这里不再全面介绍): open(file, mode, buffering, encoding, ...) .file参数:指定欲打开的文件名称,这是必须提供的参数。 . mode参数:指定打开文件后的处理方式,部分常用mode参数及功能如下。 r:以只读方式打开文件,文件的指针将会放在文件的开头。 w:写入信息。如果该文件已经存在,则先清空原有内容;如果该文件不存在,则创建新文件。 a:追加新内容,但不覆盖原有文件中的内容。 b:二进制,可以与其他模式组合使用。 t:文本模式。 +:可与其他模式混合使用。 rb:以二进制格式打开只读文件,文件指针在文件开头。 wb:以二进制格式打开文件并写入信息,文件指针在文件开头。 . buffering参数:指定读、写文件的缓存模式,0表示不缓存,1表示缓存,大于1表示缓冲区的大 小,默认是缓存模式。 .encoding参数:指定对文本文件进行编码和解码的方式,如GBK、UTF-8、CP936等,当不给出 encoding参数时,默认编码格式是UTF-8,在Windows平台下自动采用GBK(CP936)编码。 一般来说,对文件的操作包括①打开文件:使用open()语句打开文件,它会返回一个文件对象; ②对这个文件对象执行读、写等操作:读操作可使用read()、readline()、readlines()等方法实现,写操 作可使用write()、writeline()、writelines()等方法完成。 还有一种打开和关闭文件的方式是使用上下文管理语句with open(文件名,mode,encoding)asf:。 with语句块可以自动管理资源,不论什么原因跳出with代码块,它都能保证文件被正确关闭,并且可 以在代码块执行完毕后自动还原进入该代码块时的上下文,此时无须再使用close()语句关闭文件,常 用于文件操作、数据库连接、网络连接、多线程与多进程同步时的锁对象管理等场合。当然,对文件执 行什么读、写操作,还要程序设计者自己设计,with语句块并不管要读还是要写文件,它只负责执行 open()语句打开文件并在结束后自动关闭文件。例如,向打开的指定文件中追加字符串信息,下框中 132 右侧的代码一般比左侧的代码更实用。 s = 'hello' s = 'hello' f = open('文件名.txt', 'a+') with open('文件名.txt', 'a+') as f: f.write(s) f.write(s) f.close() 5.1.2 读写文本文件的基本操作 对文件的操作有很多种类,常见操作包括创建文件、删除文件、修改文件读写权限、读取文件中的 内容、将相关内容写入文件等。由于本书的科普性质,因此这里只介绍写入、读取文件操作。 读操作的常用方法有以下几种。 .read([size]):一次性读取整个文件内容,可选参数size指定读取块的大小。 .readline([size]):每次读取一行内容(包括\n字符),可选参数size指定读取块的大小。 .readlines():一次性读取整个文件内容并按行返回到列表,以便进行遍历操作。 例5.1 使用open()方法的写模式创建一个文本文件,并向其中写入自定义的一组字符串。之后 使用open()的读模式打开,并使用read()、readline()、readlines()方法读取内容,分析读到内容的区 别;再使用with语句块打开文件,并分别使用read()方法以及for循环遍历打开文件的方法,分别读取 其中的内容。 【提示与说明】 首先完成文件创建及写内容的操作。在open()方法中可定义打开文件的名称,操 作模式w是写操作模式,若该文件不存在,就创建它并把文件句柄赋值给某个变量,之后通过写文件内 容的write()方法向这个文件写入内容。代码如图5.1所示。第7行开始的代码是使用open()方法并 以读的模式打开上述文件,记得要用close()方法关闭文件。第13~20行是采用with语句块管理文 件,注意read()、readline()、readlines()方法执行后结果的区别,因为它是可迭代对象,因此可以使用 for循环显示文件内容(如第24行代码所示)。 例5.2 向一个文本文件中写入指定次数的一个字符串后,显示写入的内容。 【提示与说明】 在打开文件时指明写模式,写入用write()方法,读取用read()方法。既然是指定 次数,那么就要用到循环操作。代码实现如图5.2所示。 例5.3 当前路径下的文本文件english.txt中有3行英文文本,如图5.3所示。定义3个函数,分 别使用read()、readline()、readlines()方法读取内容并显示,要求使用read()和realine()方法时指定读 取字符块的大小。 【提示与说明】 定义3个函数,如图5.4所示。直接通过open()方法打开文件,执行完毕后,记得 使用close()方法关闭。read()、readline()方法可读取指定字符数的文本块,但readlines()方法无法指 定读取字符数的大小。 打开一个文本文件并向其中写入信息的方法,与打开一个文件并从其中读出信息的方法是类似 的,都需要通过open()方法做好准备,如图5.5所示,只不过在open()方法中要设定文件操作模式为写 模式(模式含义参见上文),使用write()方法可以写入信息。 图5.直接打开文件和使用wh语句块打开文件 1 it 图5.写入指定次数的字符串 2 图5.文本内容 3 133 图5.d()、()、s() 方法使用示例 4 reareadline readline 图5.向文件中写信息 5 要掌握对文件对象的读写操作。 以读模式打开文件并设定文件对象f“文件名”,‘’) . 1: f1=open(r .以写模式打开文件并设定文件对象f1: f1=open(“文件名”,‘w’) .从文件对象f1中读取信息并赋值给变量:str=f1.read() .关闭由f1对象指向的文件: fcoe() 1.ls 如果需要读取某个源文件中的内容并将其写入其他文件,则可在一条with语句中同时定义要读 入信息的源文件和要写入信息的目标文件,如图5.其中srsc分别代表源文件和目标文件, 6所示,c和d 注意二者的open() 方法中的模式是不一样的;srcread() 是返回读取的源文件内容并放置在缓存中, ds.rte() 则是将存于缓存的已读取的信息写入目标(.) 文件。 twi 134 图5.6同时读取源文件中的内容并写入目标文件 下面通过一个例子看看在open()方法中用来控制字符编码方式的encoding参数的用法。 图5.将一个以CP936编码的文本文件中的内容复制到另一个使用 7所示是通过定义函数的方式, UTF-8编码的文本文件中的操作。此例通过两个嵌套的with语句分别以读(第2行代码)和写(第3 行代码)的方式打开两个文件,并分别赋值给两个文件变量。在通过read()方法读出源文件内容后,将 其作为参数传递给write()方法,完成对目标文件的写入。注意第2行和第3行代码分别在open()方 法中设置了文件编码方式,其参数来源于函数的形参。在最后调用该函数时,指明了源文件、目标文 件、源文件编码方式、目标文件编码方式等。 图5.复制文件时对文件编码的转换 7 从图5.要再一次理解形式参数和实际参数的区别。 7函数定义中, 由于本书的科普性质,因此这里仅给出有关文件的常用操作方法(1所示, 如表5.这里假设文件变 量为f),有关文件操作的完整内容,可参阅相关Python编程手册。 表5.对文件的常用基本操作方法 1 方法说明 f.open() 在open()方法中指定打开某文件及其模式,并赋值给文件变量f f.close() 把缓冲区中的内容写入f文件,同时关闭f文件并释放文件对象 f.flush() 把缓冲区中的内容写入f文件(但不关闭文件) f.write(aString) 将aString表示的内容写入打开的文件f中,返回写入的字符数 f.writeline(aString) 把内容列表写入文件f中 f.writelines(aString) 把内容列表写入文件f,不添加换行符 f.read([size]) 读取f文件内容并以字符串的形式返回,size可选参数为字符数,未指定时读取所有内容 f.readline(size) 从文件f中读取一行内容作为结果返回,size指定读取块的大小 f.readlines([sizehint]) f.sek(ofset[,from]) 一次性读取整个文件内容并按行返回到列表 改变文件f的当前指针位置;ofset是相对于from的位置:from是默认值0,表示开头;1 表示当前位置;2表示文件结尾。示例如例5.4、例5.5所示 下面通过几个例题说明表5. 1中部分方法的使用。 例5.考察s方法的使用。假设有一个文本文件sl.x现在要以写模式打开它,并在 4 ek() ampett, 135 特定位置插入新的给定字符串,插入位置和插入内容可在代码中自行定义。 【提示与说明】这道题目考察seek()方法的使用。通过with语句块以写方式打开文件,采用 seek()方法定位到需要的位置上,再基于write()方法写入信息,如图5.8所示。 同理,也可以通过seek()方法定位到特定位置后读取相应长度的内容,如图5.9所示。 图5. ek()方法定位并写入信息图5. ek()方法定位并读取信息 8s9s 例5.5 假设一个文本文件datatxt中有若干用英文逗号分隔的多行数据,这些数据可能是数量不 等的整数或小数(但无字母和特殊符号(.) )。这些行的数据头、尾可能有数量不等的空格。请先清洗数 据,删除头、尾空格,之后将这些整数按升序排序后,再用分号分隔,写入另一个文本文件data_asc. txt中。 【提示与说明】首先要打开文件,可通过readlines()方法读取所有行。数据清洗时,可以用strip()方 法移除每行头、尾的指定字符,默认为移除空格或换行符,但该方法只能删除开头或结尾的字符,不能 删除中间部分的字符。为了便于统一处理这些位于多行的数,可以将它们以逗号为分隔符拼接起来, 形成一个“大串”。之后,用split()方法分隔得到一组数字字符串序列,通过类型转换为整数后,放入一 个列表中,对该列表采用sort()方法排序,最后在这些有序数据之间插入指定的分号分隔符,将排好序 的、中间添加了分隔符的这串数据写到新的文件中。伪代码如下。 例5. 5: Input:文本文件 Output:文本文件 Steps: 1.打开文件读取所有内容 2.清洗数据 1 利用读取内容的可迭代特征遍历它并分别进行处理 2. 3.将干净的数按“特定字符”拼接在一起,以便于集中处理 4.按“特定字符”进行分隔,得到纯数内容的字符串 5.类型转换为整数并存于列表中 6.对列表中的纯数据进行排序 7.在各个已经排好序的数据之间插入指定的分号作为分隔符 8.写文件 代码实现如图5.第4行代码是以逗号为分隔而合并所有 10所示。第3行代码完成无关字符清理; 行,但执行完毕后会在最后增加一个逗号进行数值类型转换,因此第5行代码的作用是将最后那个多余的 逗号删除,且由于字符串函数rstrip()执行完毕后不影响原字符串data的内容,故这里将其赋值给一个新 的变量newdata。后续完成类型转换、排序、写文件等操作,详见图5.10中的代码注释,这里不再赘述。 例5.打开含有多行英文句子的一个文本文件, 11所示。统计其最长行的英文字符数, 6 如图5.并 显示该行内容。 136 137 图5.10 综合示例(1) 图5.11 示例文本文件 【提示与说明】 首先打开这个文本文件。因为每一行的字符数长度都不一样,而列表可以存储不 同的元素类型,因此可定义一个列表,列表的第一个元素为某行的字符数,第二个元素为该行内容(某 个字符串)。采用循环机制,依次顺序读取文件中的每一行,若其长度大于已处理的行的长度,则将该 行字符内容与其长度存储在列表中,否则不变。可见,这就是一个找最大值的算法。这样,就能保证这 个列表中存储的是最长那行的长度及其对应的字符串内容。最后输出这个列表中的元素,即可得到最 长行的字符串以及字符数。伪代码如下,代码实现如图5.12所示。 例5.6: Input:文本文件 Output:含字符数最多的行的内容及对应的字符数 Steps: 1.打开文件读取所有内容 2.定义存储最终结果的列表:其第一个元素为待处理行中的字符数,第二个元素为字符串内容 3.依次读取各行内容 3.1若其长度大于列表中的第一个元素,则存储到列表,否则继续判断下一行的内容 5.1.3 读写CSV 文件的基本操作 CSV 文件(全称是Comma-SeparatedValues,意思是逗号分隔值)可以用Excel打开。鉴于本书的 科普定位,可以利用Python自带的标准CSV 模块中的reader类和writer类读写CSV 文件的数据:使 用其中的csv.reader()函数接收这个CSV 文件对象,就可以通过迭代对象的方法处理文件中的各行内 容;使用其中的csv.writer()函数可向其中写入信息。下面对这两个方法的主要参数进行简介。 138 图5.12 排序各行长度 .csv.reader(csvfile,[dialect=e' xcel'],…)。csvfile参数用于读取CSV文件;可选参数之一的dialect 是用于不同CSV变种的特定参数组。该方法的参数很多,不止列出的这两个,如需了解更多内 容,可以查询相关手册,这里不再详述。reader()方法返回一个reader对象,该对象将逐行遍历 CSV文件,CSV文件的每一行都读取为一个由字符串组成的列表(如图5.13输出结果所示)。 .csv.writer(csvfile,[dialect=e' xcel'],…)。csvfile参数是要写入信息的CSV 文件(可以是任 何具有write()方法的对象);可选参数之一的dialect是用于不同CSV 变种的特定参数组。该 方法也有其他多个参数,不再赘述。writer()方法返回一个writer对象,该对象负责将用户数 据在给定的文件类对象上转换为带分隔符的字符串。 例5.7 在当前路径下,一个名为student_scores.csv的文件中存储了多行信息,每行的属性列为 sno、sname、grade(分别代表学号、姓名、考试分数),每行数据对应于一个学生的信息。请显示该CSV 文件中各行的信息。 【提示与说明】 可使用CSV 包中的reader()方法读取该CSV 文件的内容,CSV 文件的每一行被 读取为一个由字符串组成的列表,也可将其转换为字符串。代码实现如图5.13所示。第1行代码首先 导入相关的用来处理CSV 文件的包,第3行代码为使用csv.reader()方法读取打开的CSV 文件,读取 的内容可被迭代处理(第5~8行代码的for循环)。由于本CSV 文件的第1行为属性信息,因此输出 的第0行显示的是具体的字段结构信息,而非某个具体同学的信息。 图5.13 读取CSV 文件中各行的信息 一个数据表是由横着的“行”和纵着的“列”组成的。列是结构信息,即每一行都要有的字段结 构,如“姓名性别年龄”等;而每一行是一组特定的人或物的各个属性信息,如“晓明男12”等。 列称为结构,行称为记录。 例5.向上述CSV 文件中增加一行信息,包含学号、姓名、考试成绩等信息(具体数据值可自行 在代码中给定)。 方法中以追加方式打开上述CSV 文件( 14 第2行代码中的‘ 8 【提示与说明】在open() 参见图5.a’ 模式), 并将文件对象csvie作为参数传递给cswier() 方法,可以向该文件写入信息。由于各行是 flv.rt 以列表形式存储的,因此拟增加的信息也用列表方式表示, 14 所示。 如图5. 图5.向CSV 文件中写入一行信息 14 CSV 文件的writerow() 是单行写入,即将一个列表全部写入CSV 的同一行;CSV 文件的writerows() 是多行写入,即将一个二维列表的每一个列表写为一行,适合于写入多行信息。因此,可以用writerow() 完成对数据表结构的定义(定义属性字段), 用writerows() 完成对多行信息的写入(录入多行数据)。 请使用CSV 文件的w方法,仿照图514 中的示例,一次性地在CSV 文件中插入多行数据。 riterows() . 例5.新建一个CSV 文件,写入两名同学的基本信息(班级号、姓名、性别、身高、爱好), 之后打 开该文件,显示刚才写入的信息(首行是属性信息,接下来是这两名同学的记录信息)。 【提示与说明】向一个新建的CSV 文件中写入信息时,首先要定义它的结构(属性或字段名)。 本题要求的字段结构是班级号、姓名、性别、身高、爱好,可以先用一个列表进行定义,通过writerow() 写入字段的结构信息(参见图5.3行代码)。之后, pen() 方法新建并打开该CSV 文件后, 9 15 第2、通过o 通过writerow() 写入结构,即属性信息,writerows() 一次性写入各个记录的详细信息。完成写入后, 可以使用文件的reader() 方法读取并迭代输出每一行信息以完成显示。 图5.w() 和ws() 的比较 15 writeroriterow 139