第5章 文 件 读 写 文件是计算机存储数据的重要形式,用文件组织和表达数据更加有效和灵活。文件有不同编码和存储形式,如文本文件、图像文件、音频和视频文件、数据库文件、特定格式文件等。每个文件都有各自的文件名和属性,对文件进行操作是Python的重要功能。 5.1文本文件读写 5.1.1读取文件全部内容 文本文件的读取通常有3个基本步骤: 打开文件、读取文件和关闭文件。 1. 打开文件 文件访问前必须先打开文件,并指定文件将做什么操作。Python通过内置标准函数open()打开或创建文件,函数返回文件句柄。打开文件语法如下。 1 2 3文件句柄=open('文件名', '操作模式', '编码') with open('文件名', '操作模式' ', '编码') as 文件句柄: with open(file_name1) as file1, open(file_name2) as file2, open(file_name3) as file3: (1) 文件句柄。文件读取是一个非常复杂的过程,它涉及设备(如硬盘)、通道(数据传输方式)、路径(文件存放位置)、进程(读写操作)、文件缓存(文件在内存中的存放)等复杂问题。文件句柄是函数open()的返回值,它隐藏了设备、通道、路径、进程、缓存等复杂操作,帮助程序员关注正在处理的文件。文件句柄用变量名表示(如file、file_data、f等),它通过函数open()与文件连接。文件打开或创建后,文件句柄就代表了打开的文件对象,这简化了程序设计,程序员可以方便地读取文件中的数据。 (2) 文件函数。函数open()可以打开一个文本文件或者新创建一个文本文件。函数open()参数为'文件名'、'操作模式'、编码,参数用引号括起来。 (3) 文件名。文件名表示文件的存储位置,包括文件路径和文件名。文件可以采用相对路径,如'江南春.txt'; 也可以采用绝对路径,如'd:\\test\\05\\江南春.txt'。 第 5 章 文件读写 Python应用程序设计(第2版) (4) 编码。编码指源文件的编码模式,如encoding='utf8'、encoding='gbk'等。 (5) 操作模式。操作模式即文件的读写模式,操作模式用引号括起来,如'r'、'rb'、'w'、'a'等。常用操作模式如表51所示。 表51文件打开常用操作模式 操 作 模 式参 数 说 明 r仅读。待打开的文件必须存在; 若文件不存在则会返回异常FileNotFoundError w仅写,不可读。若文件已存在则内容将先被清空; 若文件不存在则创建文件 a仅写。若文件已存在则在文件最后追加新内容; 如果文件不存在则创建文件 r+可读,可写,可追加。待打开的文件必须存在(+参数说明允许读和写) a+读写。若文件已存在,则内容不会清空 w+读写,若文件已存在,则内容将先被清空 rb仅读,读二进制文件。待打开的文件必须存在(b参数说明读二进制文件) wb仅写,写二进制文件。若文件已存在,则内容将先被清空 ab仅写,写二进制文件。若文件已存在,则内容不会清空 2. 读取文件 Python提供了read()、readlines()和readline()三个文件读取函数。这3种方法都会把文件每行末尾的'\n'(换行符)也读进来,可以用splitlines()等函数删除换行符。 (1) 函数read()一次读取文件的全部内容,返回值是一个大字符串。读取文件时会包含行末尾的换行符(\n)。它常用于文本按字符串处理,如统计文本中字符的个数。 【例51】用函数read()读取“琴诗.txt”文件中全部内容。 1 2 3 >>>f = open('d:\\test\\05\\琴诗.txt', 'r', encoding='gbk') >>>s = f.read() >>>s '[宋] 苏轼《琴诗》\n若言琴上有琴声,放在匣中何不鸣?\n若言声在指头上,何不于君指上听?\n'# 打开文件,f为文件句柄 # 读取文件全部内容 # 输出内容为字符串 # 参数\n为换行符 【例52】读取“琴诗.txt”文件中全部内容,并且删除文件中的换行符。 1 2 3 4 >>>file = open('d:\\test\\05\\琴诗.txt', 'r') >>>s = file.read() >>>s = s.splitlines() >>>s ['[宋] 苏轼《琴诗》', '若言琴上有琴声,放在匣中何不鸣?', '若言声在指头上,何不于君指上听?']# 打开文件,file为文件句柄 # 读取文件全部内容 # 删除字符串中的换行符 # 输出内容已转换为列表 # 输出已经删除换行符 (2) 函数readlines()一次读取文件的全部内容,返回值是以行为元素的字符串列表,该列表可以用for循环进行处理。注意,行可能是文本中的一个短行,也可能是一个大的段落。它常用于文本按行处理,如统计文本中字符串的行数(参见5.2.5节)。 【例53】用函数readlines()读取“琴诗.txt”文件中全部内容。 1 2 >>>file = open('d:\\test\\05\\琴诗.txt', 'r') >>>file.readlines() ['[宋] 苏轼《琴诗》\n', '若言琴上有琴声,放在匣中何不鸣?\n', '若言声在指头上,何不于君指上听?\n']# 打开文件,file为文件句柄 # 读取文件全部内容 # 输出内容为列表 (3) 函数readline()每次只读取一行,返回值是字符串。它的读取速度比readlines()慢得多,只有在没有足够内存一次读取整个文件时,才会使用readline()函数。 【例54】用函数readline()读取“琴诗.txt”文件中一行内容。 1 2 >>>file = open('d:\\test\\05\\琴诗.txt', 'r') >>>file.readline() '[宋] 苏轼《琴诗》\n' >>>file.close()# 打开文件,file为文件句柄 # 读取文件一行内容 # 输出一行字符串 # 关闭文件 3. 关闭文件 文件使用结束后一定要关闭,这样才能保存文件内容,释放文件句柄占用的内存资源。关闭文件语法如下。 1文件句柄.close() 5.1.2读取文件指定内容 1. 文件指针 文件打开后,对文件的读写有一个读取指针(元素索引号),从文件中读入内容时,读取指针不断向文件尾部移动,直到文件结束位置。Python提供了两个读写文件指针位置相关的函数tell()和seek()。它们的语法如下。 1 2文件对象名.tell() 文件对象名.seek(偏移量[, 偏移位置])# 获取当前文件操作指针的位置 # 改变当前文件操作的指针位置 函数seek()中,偏移量表示要移动的字节数,正数表示向文件尾部移动,负数表示向文件头部移动。数字、英文字符、换行符等占1字节,GBK编码下汉字占2字节。 函数seek()中,偏移位置=0表示文件开头,偏移位置=1表示当前位置,偏移位置=2表示文件结尾位置。函数seek()返回文件行指针位置。 【例55】获取文件指针位置。 1>>>file = open('d:\\test\\05\\琴诗.txt') 2>>>file.tell()# 获得当前文件读取指针 0 3>>>file.seek(10)# 将文件指针向后移动10字节 10 4>>>file.seek(0, 2)# 将文件读取指针移动到文件尾部 85# 文件大小为85字节 2. 读取文件指定行 【例56】文件“成绩utf8.txt”内容如下所示,读取文件中第2、3行。 1 2 3 4 5学号,姓名,班级,古文,诗词,平均 1,宝玉,01,70,85,0 2,黛玉,01,85,90,0 3,晴雯,02,40,65,0 4,袭人,02,20,60,0 案例分析: 用函数readlines()读出文件到列表,循环对列表指定数据进行切片。 1 2 3file_name = 'd:\\test\\05\\成绩utf8.txt' with open(file_name, 'r', encoding='utf8') as file: lines = file.readlines() # 路径赋值(绝对路径) # 打开文件(文件编码为UTF8) # 读取全部文件到列表lines 4 5for i in lines[1:3]: print(i.strip()) # 循环对列表切片,读取文本行 # 函数strip()为删除字符串两端的空格 >>> 1,宝玉,01,70,85,0 2,黛玉,01,85,90,0 # 程序输出 程序说明: 程序第4行,语句for i in lines[1:3]中,[1:3]为列表切片索引号位置。其中,0行是表头,不读取,列表第3行不包含,因此语句功能为循环读取列表第1、2行。 程序扩展: 如果希望对文件数据隔一行读一行时,只需要修改程序第5行中列表索引号即可,如“for i in lines[1:4:2]:”。语句表示读取列表第1~4行,步长=2(即读第1、3行)。 【例57】读取“成绩utf8.txt”文件中第2行的内容。 案例分析: 只要文件是UTF8编码,都可以用标准模块linecache中函数getline()读出文件中的指定行。函数语法为linecache.getline(文件名,行号)。 1 2 3import linecache s = linecache.getline('d:\\test\\05\\成绩utf8.txt', 2) print('第2行:', s)# 导入标准模块——行读取 # 读取文件第2行 >>>第2行: 1,宝玉,01,70,85,0# 程序输出 【例58】读取“梁山108将gbk.txt”文件,然后随机打印5个人。 1 2 3 4 5 6 7 8import random n = 1 file = open('d:\\test\\05\\梁山108将gbk.txt', 'r') members =file.readlines() while n <= 5: winner = random.choice(members) print(winner) n = n+1# 导入标准模块——随机数 # 计数器初始化 # 打开文件 # 按行读取文件 # 随机返回列表中的一个元素 # 打印人物姓名 # 计数器自增 >>>段景住 扈三娘 穆春 鲁智深 呼延灼# 程序输出 5.1.3文件内容遍历 文件遍历就是读取文件中每个数据,然后对遍历结果进行某种操作,如输出遍历结果、将遍历结果赋值到某个列表、对遍历结果进行统计(如字符数、段落数等)、对遍历结果进行排序、将遍历结果添加到其他文件等操作。遍历是一个非常重要的操作。 1. 文件遍历方法 用open()或with open()都可以实现文件遍历,它们的差别如下。 (1) 用open()读取文件后,需要用close()关闭文件; 用with open()读取文件结束后,语句会自动关闭文件,不需要再写close()。 (2) 用open()读取文件如果发生异常,则没有任何处理功能; 而with open()会处理好上下文产生的异常。 (3) open()一次只能读一个文件; with open()一次可以读多个文件。 【例59】文件遍历1: 逐行读取文件内容。 1 2with open('登鹳雀楼.txt','r') as file: content = file.read()# 打开文件(相对路径,当前目录在d:\test\05) # 循环读取文件中每一行 3print(content) # 输出行内容 >>>[唐] 王之涣…# 程序输出(略,输出无空行) 【例510】文件遍历2: 一次全部读入文件内容到列表,再逐行遍历列表。 1 2 3 4 5 6file = open('d:\\test\\05\\登鹳雀楼.txt', 'r') lst = file.readline() while lst: print(lst, end='') lst = file.readline() file.close()# 打开文件(绝对路径),'r'为读操作 # 将文件内容一次全部读入列表lst # 循环输出列表lst中的内容 # 参数end=''为不换行输出 # 读取列表中行的内容 # 关闭文件 >>>[唐] 王之涣…# 程序输出(略,输出无空行) 【例511】文件遍历3: 循环读取文件内容到列表,再输出列表。 1 2for lst in open('登鹳雀楼.txt','r'): print(lst)# 打开文件,循环输出列表内容(相对路径) # 输出列表内容 >>>[唐] 王之涣…# 程序输出(略,输出有空行) 【例512】文件遍历4: 文件内容一次全部读入列表,再逐行遍历列表内容。 1 2 3 4 5file = open('d:\\test\\05\\登鹳雀楼.txt', 'r') lst = file.readlines() for s in lst: print(s, end='') file.close()# 打开文件(绝对路径),r为读操作 # 读取文件全部内容到列表 # 循环读取列表中的行 # 输出行内容(没有end=''参数会多输出一些空行) # 关闭文件 >>>[唐] 王之涣…# 程序输出(略,输出无空行) 【例513】文件遍历5: 一次读取2个文件全部内容到列表,再输出列表。 1 2 3 4with open('d:\\test\\05\\金庸名言1.txt', 'r') as file1, open('d:\\test\\05\\金庸名言2.txt', 'r') as file2: print(file1.read()) print(file2.read()) # 读入文件1 # 读入文件2 # 打印第1个文件 # 打印第2个文件 >>> 侠之大者,为国为民。 ——金庸《射雕英雄传》 只要有人的地方就有恩怨,有恩怨就会有江湖,人就是江湖。 ——金庸《笑傲江湖》# 程序输出 【例514】文件遍历6: 读取“成绩utf8.txt”文件,打印内容和统计行数。 案例分析: 函数enumerate()的功能是遍历一个序列(参见3.3.1节),可以用它显示文件内容,用循环计数的方法统计文件行数。 1 2 3 4 5 6 7file = open('d:\\test\\05\\成绩utf8.txt', 'r', encoding='utf8') count = 0 for index, value in enumerate(file): count += 1 print(f'{index}:{value}') file.close() print('文件行数为:', count)# 打开文件,定义文件编码 # 计数器初始化 # 循环读取文件(元组变量) # 行数累加 # 打印索引号和行内容 # 关闭文件 # 打印文件并统计行数 >>> 0:学号,姓名,班级,…# 程序输出(略,输出有空行) 程序说明: 程序第1行,文件必须为UTF8编码,而且设置编码参数encoding='utf8'。 程序第3行,函数enumerate()返回的迭代变量是元组。 2. 用if语句判断文件结束 【例515】用if语句判断文件是否结束。 1 2 3 4 5 6 7 8 9file_path = 'd:\\test\\05\\登鹳雀楼.txt' file = open(file_path, 'r') while True: line = file.readline() if (line != ''): print(line) else: break file.close()# 路径变量赋值 # 读取文件全部内容,file为文件句柄,r为读取模式 # while永真循环 # 读取文件行 # 如果line不等于空,则文件没有结束 # 打印行内容 # 强制退出循环 # 关闭文件 >>>[唐] 王之涣…# 程序输出(略,输出有空行) 程序说明: 程序第5行,语句if (line!='')为判断第4行readline()读到的内容是否为空,行内容为空意味着文件结束。如果语句readline()读到一个空行,也会判断为文件结束吗?事实上空行并不会返回空值,因为空行的末尾至少还有一个换行符(\n)。所以,即使文件中包含空行,读入行的内容也不为空,这说明语句if (line!='')判断是正确的。 5.1.4文件写入数据 1. 覆盖写入文件 Python提供了两个文件写入函数,语法如下。 1 2文件句柄.write('单字符串') 文件句柄.writelines('行字符串')# 语法1:向文件写入一个字符串或字节流 # 语法2:将多个字符串元素写入文件 函数write()是将字符串写入一个打开的文件。注意,字符串也可以是二进制数据; 函数write()不会在字符串结尾添加换行符(\n)。 【例516】用函数write()将字符串内容写入名为“杜甫诗歌1.txt”的文件。 1 2 3 4 5str1 = '百年已过半,秋至转饥寒。\n为问彭州牧,何时救急难?\n' file = open('d:\\test\\05\\杜甫诗歌1.txt', 'w') file.write(str1) file.close() print('字符串写入成功。') # 符号\n为换行符 # 以写模式打开文件 # 字符串内容写入文件 # 关闭文件 >>>字符串写入成功。# 程序输出 程序说明: 程序第1行,由于函数write()不会在字符串结尾自动添加换行符(\n),因此字符串中必须根据需要人为加入换行符。 程序第2行,如果这个文件已经存在,那么源文件内容将会被新内容覆盖。 【例517】用函数writelines()将内容写入名为“寄征衣out.txt”的文件。 1 2s = ['欲寄君衣君不还,', '不寄君衣君又寒。', '寄与不寄间,', '妾身千万难。\n —— [元] 姚燧《寄征衣》']# 定义列表 # 写模式打开文件 3 4 5 6file = open('d:\\test\\05\\寄征衣out.txt', 'w') file.writelines(s) file.close() print('列表写入成功。') # 列表写入文件 # 关闭文件 >>>列表写入成功。# 程序输出 2. 追加写入文件 【例518】将字符串内容追加写入“杜甫诗歌1.txt”文件的结尾。 1 2 3 4file = open('d:\\test\\05\\杜甫诗歌1.txt', 'a+') file.write(' ——杜甫《因崔五侍御寄高彭州一绝》\n') file.close() print('追加写入成功。') # 以追加模式打开已存在的文件 # 将字符串内容追加写入文件末尾 # 关闭文件 >>>追加写入成功。# 程序输出 3. 二进制文件的读写 【例519】读取“图片.png”文件,并且写入“图片复制.png”文件。 1 2 3 4 5file_read = open('d:\\test\\05\\图片.png', 'rb') file_write = open('图片复制out.png', 'wb') file_write.write(file_read.read()) file_write.close() file_read.close()# 以二进制读方式打开文件 # 以二进制写方式创建文件 # 读二进制文件并写二进制文件 # 关闭写文件 # 关闭读文件 >>># 程序输出 5.2常用文件操作 5.2.1文件的格式化 1. 结构化数据和非结构化数据 结构化数据也称为行数据,它有规定的数据类型、规定的存储长度、规范化的数据结构等要求,可以用数据库进行存储和管理。常见的结构化数据有数据库文件、Excel文件、CSV文件、部分结构规范的文本文件等。结构化数据非常适合程序进行处理。 非结构化数据主要有办公文档(文档编码不一)、数据结构不一的文本文件(如字符串、数值、日期格式不一致)、网页(各种HTML标签和控制符)、各类图片、音频、视频等数据。非结构化数据不适宜用关系数据库进行存储和管理,程序处理非结构化数据非常麻烦。数据清洗的主要工作是将非结构化数据转换为结构化数据,便于程序处理。 2. 英文字母大小写转换 【例520】将莎士比亚(Shakespeare)名言中的英语单词进行各种转换。 1 2 3 4 >>>char='The pyramid is built with stones pieces of.' >>>print(char.upper()) THE PYRAMID IS BUILT WITH STONES PIECES OF. >>>print(char.lower()) the pyramid is built with stones pieces of. >>>print(char.capitalize()) The pyramid is built with stones pieces of. # 金字塔是用一块块石头堆砌而成 # 所有字符转换为大写字母 # 所有字符转换为小写字母 # 语句第一个字母转换为大写字母 # 其余为小写 5 >>>print(char.title()) The Pyramid Is Built With Stones Pieces Of. # 每个单词的第一个字母转换为大 # 写,其余为小写 3. 文本格式对齐 【例521】如图51所示,文件“成绩1.txt”中文本行中的空格参差不齐,试对文本行中的空格进行对齐处理(见图52)。 图51成绩1.txt 图52对齐后的文本格式 案例分析: 图51中,行中空格数参差不齐,可以用函数split()对字符串进行切分(参见2.1.3节),将每行切分为3个元素; 然后用函数format()对一行中的字符串进行格式化排列对齐(参见3.1.3节)。 1 2 3 4 5with open('d:\\test\\05\\成绩1.txt', 'r') as file: for s in file: L = s.split() t = '{0:<4}{1:<5}{2:4}'.format(L[0], L[1], L[2]) print(str(t)) # 用绝对路径打开文件 # 用空格符对字符串进行切分 # 字符串格式化对齐 >>>01 贾宝玉 85.5 …# 程序输出见图5-2(略) 程序说明: 程序第4行,参数{0: <4}为0号元素(序号),占4个字符; 参数{1: <5}为1号元素(姓名),占5个字符; 参数{2: 4}为2号元素(成绩),占4个字符。格式化函数format()中,L[0]为0号元素,L[1]为1号元素,L[2]为2号元素。 5.2.2多个文件合并 文本处理时,往往需要将多个文件合并在一起,再进行数据处理。多个文件合并时,先创建一个新文件,其次读入文件A,然后将文件B添加在文件A之后,文件C添加在文件B之后,以此类推。文件合并后,还需要对合并后的文件进行清理,如排列格式化、删除空格、删除空行、删除乱码等操作。 【例522】文件如图53~图55所示,将“琴诗1.txt”“琴诗2.txt”“琴诗3.txt”文件合并成一个文件。 图53琴诗1.txt 图54琴诗2.txt 图55琴诗3.txt 案例分析: 多个文本文件合并时,首先将所有文件合并成一个新文件,然后对新文件进行格式化处理,如删除多余的空行、对齐文本行、对文件乱码进行处理等。 (1) 合并文件。 1 2file_list = ['d:\\test\\05\\琴诗1.txt', 'd:\\test\\05\\琴诗2.txt',# 定义文件列表 3 4 5 6 7 8'd:\\test\\05\\琴诗3.txt'] file = open('d:\\test\\05\\琴诗4.txt', 'w') for data in file_list: for txt in open(data, 'r'): file.write(txt + '\r') file.close() # 创建新的临时文件琴诗4.txt # 外循环遍历读取源文件列表 # 内循环读取某个文件中的每一行 # 行内容写入文件,行尾加回车符(\r) # 关闭文件 >>># 程序输出 程序说明: 用记事本程序查看新创建的“琴诗4.txt”文件,会发现文件中出现了多余的空行,因此需要进行删除空行的处理。 (2) 删除文件“琴诗4.txt”中的空行。 1 2 3 4 5 6 7 8 9file = open('d:\\test\\05\\琴诗4.txt', 'r') tup = list() for line in file.readlines(): line = line.strip() if not len(line) or line.startswith('#'): continue tup.append(line) open('琴诗5.txt', 'w').write('%s' % '\n'.join(tup)) file.close()# 以读方式打开临时文件 # 函数list()将元组转换为列表 # 循环读取文件中每一行 # 删除每行头尾的空白字符 # 判断是否为空行,或是以#开始的行 # 空行跳过不处理,返回循环头部 # 插入一个新行 # 创建新文件,并将内容写入文件 # 关闭文件 >>># 程序输出 程序说明: 程序第4行,函数line.strip()用于删除指定字符,如果没有指定字符则删除空白字符(包括换行符'\n'、回车符'\r'、制表符'\t'、空格' ')。函数从原字符尾部开始寻找,找到指定字符就将其删除,直到遇到一个不是指定字符就停止删除。 程序第5行,函数startswith('#')用于检查字符串是否以'#'子字符串开头,如果是则返回True,否则返回False。也可以用“if line.count('\n')==len(line)”判断是否为空行。 程序第8行,函数write('%s' %'\n'.join(s))中,%s为字符串占位符(参见3.1.3节); %'\n'为换行占位符; 函数join(s)用于将字符串s连接成一个新字符串。 5.2.3多个文件连接 文件连接与文件合并的相同之处是两个文件合并形成一个新文件,不同之处是文件合并是文件B连接在文件A之后,而文件连接是文件A与文件B的第1行连接在一起,文件A与文件B的第2行连接在一起,以此类推,最后形成一个新文件。 文件连接有以下要求: 一是两个文件的行与行之间为一一对应的关系; 二是两个文件的行数一致; 三是两个文件的编码一致(如均为UTF8编码)。 【例523】“企业A.txt”记录了企业职工工号和姓名(见图56); “企业B.txt”记录了职工工号和工资(见图57)。要求将两个文件按行连接,即企业A的第1行连接到企业B的第1行,其余以此类推; 然后打印输出连接后的文件。 案例分析: 首先创建“企业A.txt”和“企业B.txt”的文件句柄; 其次创建一个新的输出文件“企业AB_out.txt”; 然后循环读入文件句柄a中的一行,与文件句柄b中的一行连接在一起; 最后将连接好的新行写入新建文件即可。 图56企业A.txt(工号,姓名) 图57企业B.txt(工号,工资) 1 2 3 4 5 6 7 8 9 10file_a = open('d:\\test\\05\\企业A.txt', 'r', encoding='utf8') file_b = open('d:\\test\\05\\企业B.txt', 'r', encoding='utf8') new_file = open('d:\\test\\05\\企业AB_out.txt', 'a', encoding='utf8') for i in file_a: line_c = i.strip() + ' ' + file_b.readline().strip() + '\n' new_file.write(line_c) print(line_c) file_a.close() file_b.close() new_file.close()# 创建文件句柄 # 创建新文件,追加模式 # 循环写入文件内容 # 连接文件行(很重要) # 连接好的行写入新文件 # 打印连接后的新行 # 关闭文件 >>> 100 Jason Smith(杰森·史密斯) 100 $5000 200 John Doe(约翰·多伊) 200 $500 300 Sanjay Gupta(桑贾伊·古普塔) 300 $3000 400 Ashok Sharma(阿肖克·夏尔马) 400 $1250# 连接文件如下 程序说明: 程序第5行,变量line_c为连接后的新行; 函数i.strip()为删除文件a中一行字符串的前后空格; 参数''为元素之间添加2个空格; 函数file_b.readline().strip()为读取文件b中的一行,并删除前后空格; 参数'\n'为在行尾添加换行符。 从程序输出结果可见,工号在同一行中有重复,虽然可以用集合函数将重复元素删除,但是集合的无序性会导致行中元素混乱,因此对重复元素需要另外编程处理。 【例524】文件内容如图58~图510所示,将三个文件连接成一个新文件,要求将文件按行连接,即test1.txt第1行后面连接test2.txt第1行,其余以此类推。 图58test1.txt 图59test2.txt 图510test3.txt 案例分析: 以下解决方案比例523复杂,程序用了函数zip(*files)进行解包。 1 2 3 4 5 6 7 8 9import itertools as it file = 'd:\\test\\05\\test1.txt d:\\test\\05\\test2.txt d:\\test\\05\\test3.txt'.split() with open('d:\\test\\05\\test_out.txt', 'w', encoding='utf8') as out_file: files = [open(fname, 'r', encoding='utf8') for fname in file] for text in it.zip_longest(*files): out_file.write('\t'.join(t.strip() if t else '' for t in text) + '\n') for f in files: f.close()# 导入标准模块 # 定义文件变量 # 创建新文件 # 读入文件 # 用函数zip()解包 # 新行写入 # 循环关闭文件 >>># 程序输出 程序说明: 程序第5行,读入test1.txt、test2.txt、test3.txt三个文件(均为UTF8编码),注意读取和写入文件时采用UTF8编码(encoding='utf8'),否则程序会异常退出。 程序第6行,函数it.zip_longest(*files)为解包(参见4.1.2节)。 5.2.4文件内容去重 网络上下载的文件(如文本小说、密码字典等)有时会存在大量的重复行、空行、空格等; 多个文件合并时,合并后的文件中可能会含有相同的内容。因此,需要对文本进行清洗,消除重复行(去重)、空行、空格等。 对两个内容疑似相同的文件,可以通过函数md5()计算两个文件的哈希值,通过哈希值比较,判断文件内容是否相同,然后删除重复文件。 在一个文本文件内,内容去重的方法很多: 一是利用set()集合函数删除重复数据; 二是利用正则表达式删除重复数据; 三是利用程序语句删除文件中指定行或指定内容; 四是将文件A内容读入列表变量中,然后读入文件B的一行内容,如果这行内容没有出现在列表变量中,则将这行内容写入新文件C中。 【例525】如图511所示,对“成绩2.txt”文件进行去重操作,要求: ①删除空行; ②删除重复行; ③删除空格; ④生成一个新文件(见图512)。 图511源文件“成绩2.txt” 图512去重后新文件“成绩3.txt” 案例分析: 首先创建一个新文件,读入源文件中的行,删除字符串前后的空格(行中间空格后面再处理),然后写入临时文件1中。再创建临时文件2,读入临时文件1中的行,删除其中的重复行、空行、行中间的空格等,将数据写入临时文件2即可。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19file1 = open('d:\\test\\05\\成绩2.txt', 'r' , encoding='utf8') file_new = open('d:\\test\\05\\成绩tmp1.txt', 'w') for line in file1.readlines(): data = line.strip() if len(data) != 0: file_new.write(data) file_new.write('\n') file1.close() file_new.close() tmp_file = open('d:\\test\\05\\成绩tmp2.txt', 'w') lst1 = [] for line in open('d:\\test\\05\\成绩tmp1.txt', 'r'): tmp = line.strip() if tmp not in lst1: lst1.append(tmp) tmp_file.write(line) tmp_file.close() #【1.删除空行】 # 创建临时文件1 # 循环读入每一行 # 删除本行字符头尾空格 # 如果此行长度不等于0 # 写入本行数据 # 写入换行符 # 关闭源文件 # 关闭临时文件 #【2.删除重复行】 # 创建临时文件2 # 建立空列表lst1 # 循环读入临时文件1中字符 # 删除字符串首尾的空格 # 判断是否为重复行 # 在列表末尾添加新对象 # 逐行写入临时文件 # 关闭临时文件 #【3.删除空格】 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 lst2 = [] with open('d:\\test\\05\\成绩tmp2.txt', 'r') as file2: data = file2.read() s1 = data.split(' ') lines = s1 for i in lines: if i != '\r': k = i.strip() if i != '\n': k = i.replace('\t', '') lst2.append(k) with open('d:\\test\\05\\成绩3out.txt', 'w') as file3: for i in lst2: file3.write(i) # 建立空列表lst2 # 打开临时文件2 # 读临时文件2 # 通过空格对字符串进行切片 # 循环替换 # 是否不为回车符 # 不是回车符时删除头尾空格 # 是否不为换行符 # 不是换行符时删除空格 # 在列表末尾添加新对象 #【4.写入新文件】 # 创建新文件 # 写入处理过的内容到'成绩3.txt' >>># 程序输出(生成文件见图5-12) 程序说明: 程序第4行,函数line.strip()用于删除字符串头尾指定字符(默认为空格或换行符),返回一个新字符串。注意,该函数只能删除开头或结尾的字符,不能删除中间的字符。 程序第23行,函数data.split(' ')为指定分隔符(如空格符)对字符串进行切片,如果不指定分隔符,默认分隔符为换行符(\n)、回车符(\r)、制表符(\t)。 程序第29行,函数i.replace('\t','')表示把旧字符串(此处为\t,即空格)替换成新字符串(此处新字符为'',即删除空格)。 5.2.5案例: 文件字符统计 【例526】统计“全唐诗.txt”文本中的字数和行数。 1 2 3 4 5 6with open('d:\\test\\05\\全唐诗.txt', encoding='utf8') as file1: word = file1.read() print('全唐诗总字数:', len(word)) with open('d:\\test\\05\\全唐诗.txt', encoding='utf8') as file2: lines = file2.readlines() print('全唐诗总行数:', len(lines))# 打开当前目录下的文件 # 读取全部字符 # 打印全部字符数 # 打开当前目录下的文件 # 按行读入文件 # 打印文件行数 >>> 全唐诗总字数: 4646026 全唐诗总行数: 387171# 程序输出 【例527】统计某个字符串中汉字、英文、空格、数字、标点个数。 案例分析: (1) 用string.ascii_letters方法生成英文大小写字母,然后判断字符串中是否含有英文字母; 用函数isdigit()判断字符串中是否含有数字; 用函数isspace()判断字符串中是否含有空格; 用函数isalpha()判断字符串中是否有汉字; 其余字符串为标点符号。 (2) 函数有多个返回值时,可以用命名元组的方法使用返回值。 1 2 3import string from collections import namedtuple # 导入标准模块——字符串模块 # 导入标准函数——命名元组 4 5 6 7 8 9 10 11 12 13 14 15 16 17def str_count(s): en = dg = sp = zh = pu = 0 for c in s: if c in string.ascii_letters: en += 1 elif c.isdigit(): dg += 1 elif c.isspace(): sp += 1 elif c.isalpha(): zh += 1 else: pu += 1 total = zh + en + sp + dg + pu # 定义字符统计函数 # 变量初始化 # 循环统计各种字符数 # 生成a~z、A~Z字母并且判断 # 英文字符统计 # 判断字符串中是否含有数字 # 数字统计 # 判断字符串中是否含有空格 # 空格统计 # 判断是否有其他语言中的字母 # 中文字符统计 # 标点符号统计 # 统计所有字符 18 19 20 21 22 23 return namedtuple('Count', ['total','zh','en','space','digit','punc'])(total,zh,en,sp,dg,pu) # 返回值用命名元组(很重要) s = '66The pyramid is built with stones pieces of. 金字塔是用一块块石头堆砌而成88。' count = str_count(s) # 调用字符统计函数 print(f'共{count.total}个字符,其中{count.zh}个汉字,{count.en}\ 个英文,{count.space}个空格,{count.digit}个数字,{count.punc}个标点符号') >>>共63个字符,其中14个汉字,35个英文,8个空格,4个数字,2个标点符号 程序说明: 程序第7行,函数string.ascii_letters生成所有a~z、A~Z的字符串。 程序第18行,函数namedtuple()为命名元组,普通元组中的元素只能按索引号访问,不能为元组中的每个元素命名。命名元组可以构造一个带字段名的元组,命名元组有两个参数: 参数'Count'是元组名; 参数['total','zh','en','space','digit','punc']是每个元素的名称。参数(total,zh,en,sp,dg,pu)是函数中的变量名,它们与元素名称一一对应。 【例528】命名元组应用案例。 1>>>from collections import namedtuple# 导入标准函数——命名元组 2>>>user = namedtuple('user', ['name', 'lesson', 'score'])# 定义命名元组,['字段名1', …] 3>>>s1 = user('宝玉', '古文', '60')# 实例化命名元组 4 >>>print(s1.name, s1.score) 宝玉 60# 通过类属性user获取值 5 >>>print(s1._fields) ('name', 'lesson', 'score')# 通过_fields获取获字段名 5.3文本编码处理 5.3.1字符集的编码 字符集是各种文字和符号的总称,它包括文字、符号、图形、数字等。字符集种类繁多,每个字符集包含的字符个数不同,编码方法不同。如ASCII(America Standard Code for Information Interchange,美国信息交换标准码)、GBK(国家标准扩展的汉语拼音)字符集、Unicode(统一码)字符集等。计算机要处理各种字符集的文字,就需要对字符集中每个字符进行唯一性编码,以便计算机能够识别和存储各种文字。 1. Unicode字符集和编码 (1) Unicode字符集。Unicode是一个信息领域的国际字符集标准。Unicode字符集目前有221=2097152个编码(理论值)。Unicode为全球每种语言和符号中的每个字符都规定了一个唯一代码点和名称,如“汉”字的名称和码点是“U+6C49”(U为名称,6C49为码点)。Unicode目前规定了17个语言符号平面(大约110万编码),如CJK(中日韩统一表意文字)平面收录了中文简体汉字、中文繁体汉字、日文假名、韩文谚文、越南喃字。Unicode字符集有多种编码,如UTF8(UTF81,UTF82、UTF83、UTF84)、UTF16、UTF32等,其他大多数字符集(如GB2312、ASCII等)都只有一种编码。Unicode字符集中汉字码点按《康熙字典》的偏旁部首和笔画数排列,编码排序与GB(国家标准)字符集排序不同。 【例529】打印字符串的Unicode编码。Python程序如下。 1str = input('请输入一个字符串:')# 输入字符串 2a = [0]*len(str)# 计算字符串长度 3i = 0# 计数器初始化 4for x in str:# 循环计算编码 5a[i] = hex(ord(x))# 将字符x转换为Unicode编码 6i = i+1# 计数器累加 7result = list(a)# 转换为列表 8print('字符串的Unicode编码为:', result)# 打印Unicode编码 >>># 程序输出 请输入一个字符串:a中国 字符串的Unicode编码为: ['0x61', '0x4e2d', '0x56fd']# 输出字符串的十六进制编码 (2) UTF8编码。UTF8采用变长编码。UTF81编码中,128个ASCII字符只需要1字节; UTF82编码中,带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文等需要2字节; UTF83编码中,汉字为3字节; UTF84编码中,其他辅助平面符号为4字节。Python 3.x程序采用UTF8编码,Linux系统、因特网协议、浏览器等均采用UTF8编码。 (3) UTF16编码。UTF16采用2字节或4字节编码。Windows内核采用UTF16编码,但支持UTF16与UTF8的自动转换。UTF16编码有Big Endian(大端字节序)和Little Endian(小端字节序)之分,UTF8编码没有字节序问题。 (4) UTF32编码。UTF32采用4字节编码,由于浪费存储空间,因此很少使用。 2. 中文字符集标准 (1) GB 2312编码。GBK 2312是最早的国家标准中文字符集,它收录了6763个常用汉字和符号。GBK 2312采用定长2字节编码。 (2) GBK编码。GBK是GB 2312的扩展,加入了对繁体字的支持,兼容GB 2312,也与Unicode编码兼容。GBK使用2字节定长编码,共收录21003个汉字。 (3) GB 18030编码。GB 18030与GB 2312和GBK兼容。GB 18030共收录70244个汉字,它采用变长多字节编码,每个汉字或符号由1~4字节组成。Windows 7/8/10默认支持GB18030编码。 (4) 繁体中文Big5编码。Big5是港澳台地区繁体汉字编码。它对汉字采用2字节定长编码,一共可表示13053个中文繁体汉字。Big5编码的汉字先按笔画再按部首进行排列。Big5编码与GB系列编码互不兼容。 5.3.2字符编码转换 1. 字符的编码和解码 Python程序中定义的字符串默认为UTF8编码(即Unicode码),字符串解码函数为decode(),字符串编码函数为encode()。函数encode()的作用是将字符串编码为字节码(bytes),函数decode()的作用是将字节码解码成字符串,语法如下。 1decode([解码标准], [errors='ignore']) 2encode([编码标准], [errors='ignore']) 参数“解码标准”有utf8(也可写为utf8、utf、UTF8、UTF8)、gbk等。 参数errors='ignore'为忽略非法字符,或者设置为errors='replace'(用?号取代非法字符); 如果为errors='strict'(默认),则表示遇到非法字符时抛出异常。 【例530】字符串的编码与解码。 1>>>s1_utf8 = '汉字hz'# 定义字符串,默认为UTF8编码 2>>>s2_utf8 = s1_utf8.encode('utf8')# 对字符串进行编码 3>>>print(s2_utf8)# 打印字符串编码 b'\xe6\xb1\x89\xe5\xad\x97hz'# 字符串的UTF8编码 4>>>s3_utf8 = s2_utf8.decode('utf8')# 对字符串进行解码 5 >>>print(s3_utf8) 汉字hz# 打印解码后的字符串 【例531】网址中的“%xx”字符编码转换。 1>>>url = 'https://www.baidu.com/s?wd=code520中国'# 定义URL 2>>>from urllib.parse import quote# 导入标准函数——字符编码 3 >>>url_utf = quote(url, safe=';/?:@&=+$, ', encoding='utf8') # ';/?:@&=+$, '字符不编码 4>>>print('url_utf编码:%s' % url_utf) # 打印UTF编码的URL url_utf编码:https://www.baidu.com/s?wd=code520%E4%B8%AD%E5%9B%BD 5>>>from urllib import parse# 导入标准函数——编码转字符 6>>>print(parse.unquote('https://www.baidu.com/s?wd=code520%E4%B8%AD%E5%9B%BD')) https://www.baidu.com/s?wd=code520中国 程序说明: 程序第3行,函数quote()将url中的字符转换为“%xx”形式; 参数safe='; /?: @&=+$,'为特殊字符(;/?: @&=+$)不转换为“%xx”形式; 字符串编码为UTF8。 程序第5行,函数urllib.parse.unquote()将url中的“%xx”序列解码为Unicode字符; url必须是字符串; 默认编码为UTF8。 2. GBK与UTF8编码的转换 【例532】字符串的GBK与UTF8编码的相互转换。 案例分析: 当字符串为GBK编码,希望转换为UTF8编码时,可以采用函数s.decode('gbk').encode('utf8'); 当字符串为UTF8编码,希望转换为GBK编码时,可以采用函数s.decode('utf8').encode('gbk')。 1>>>s = '汉字hz'# 字符串默认为UTF8码 2>>>s_gbk = s.encode('gbk')# 无须解码,直接编码为GBK 3>>>s_gbk b'\xba\xba\xd7\xd6hz' 4>>>gbk_utf8 = s_gbk.decode('gbk', 'ignore').encode('utf8')# 字符串的GBK编码 5>>>gbk_utf8# 解码GBK,编码UTF8 b'\xe6\xb1\x89\xe5\xad\x97hz' 6>>>s_utf8 = gbk_utf8.decode('utf8')# 字符串的UTF8编码 7>>>s_utf8# 将GBK解码为UTF8 '汉字hz'# 字符串的UTF8编码 程序说明: 程序第4行,函数s_gbk.decode('gbk','ignore').encode('utf8')中,参数'gbk'为读取GBK编码文件,参数'ignore'为忽略非法字符; 解码后的字符串编码为UTF8。 5.3.3文件编码转换 1. Windows文件编码形式 Windows系统默认采用GBK编码,但是系统可以进行GBK与Unicode编码的处理。Windows下部分软件可以自动识别文件的编码形式,如“记事本”软件; 但是大部分软件没有自动识别文件编码功能,如“写字板”软件。 【例533】用“记事本”程序打开test_utf8.txt文件时,程序可以自动识别文件的编码形式,不会发生乱码(见图513); 但是用“写字板”程序打开test_utf8.txt文件时,由于写字板程序不能自动识别文件编码,因此文件会显示出现乱码(见图514)。 图513用“记事本”程序打开test_utf8.txt文件 图514用“写字板”程序打开test_utf8.txt文件 记事本程序如何确定文件编码呢?记事本程序的处理方法是在文件最前面保存一个编码标签。程序检查到文件头部标签是FF FE时,说明文件采用UTF16LEB编码(小端Unicode码); 如果文件头部标签是FE FF,则文件采用UTF16BE编码(大端Unicode码); 如果头部标签是EF BB BF,则是UTF8编码(注意,头部标签不是UTF8标准的规定,这个头部标签也很容易导致Python程序出现异常); 没有以上3个头部标签的文件是ANSI编码,如果系统是简体中文Windows,ANSI编码就是GBK编码。 2. 检查文件的编码格式 不知道文件的编码格式时,文件打开时会无法设置编码格式,使得读取的内容出现乱码。可以用第三方软件包chardet检测文件编码格式,软件包chardet安装方法如下。 1>pip install -i https://pypi.tuna.tsinghua.edu.cn/simple chardet# 版本为2.1.1 【例534】新建测试文件“江南春.txt”,保存时编码为ANSI(即GBK编码)。 1import chardet# 导入第三方包——编码 2file = open('d:\\test\\05\\江南春.txt', 'rb')# 二进制读文件 3data = file.read()# 读入文件内容 4print(chardet.detect(data))# 打印文件编码 5file.close()# 关闭文件 >>>{'encoding': 'GB2312', 'confidence': 0.711, 'language': 'Chinese'} # GB 2312可信度为71% 程序说明: 程序第2行,采用字节码读模式(rb)打开文件可以避免很多读错误,指定编码格式反而可能报错。测试文件内容过少时,检测的语言可能会有偏差。 3. GBK编码文件与UTF8文件的相互转换 【例535】将d:\test\05\目录下的“江南春gbk.txt”文件转换为UTF8编码,将文件重命名为“江南春utf8.txt”。 案例分析: 中文Windows下用函数open()打开文件时,如果没有传递encoding参数,Python会自动采用GBK编码打开文件,这容易引起读取文件时出错。解决方法是在open()函数中传递编码参数encoding='gbk',或者encoding='utf8'。 1 with open('d:\\test\\05\\江南春.txt', encoding='gbk', errors='ignore') as file: # 以GBK编码打开文件 2while True: # while永真循环 3data = file.read() # 读取文件内容 4if data: 5 open('d:\\test\\05\\江南春utf8.txt', 'a', encoding='utf8', errors='ignore').write(data) 6else: 7break # 强制退出循环 8print('文件GBK转换为UTF8完成') >>>文件GBK转换为UTF8完成 # 程序输出 【例536】将当前目录下的“江南春utf8.txt”文件转换为GBK编码,将文件重命名为“江南春gbk.txt”。 1def utf8_to_gbk(inFilePath, outFilePath):# 定义转换函数 2with open(inFilePath, 'rb') as file1:# 创建文件(二进制码) 3a = file1.read()# 读取文件内容 4b = a.decode('utf8', 'ignore')# 文件编码 5with open(outFilePath, 'w', encoding='gbk') as file2:# 创建新文件(GBK编码) 6file2.write(b)# 写入文件内容 7print('文件转换完成') 8 9utf8_to_gbk('d:\\test\\05\\江南春utf8.txt','江南春gbk.txt')# 调用转换函数 >>>文件转换完成# 程序输出 5.3.4文本乱码处理 1. 文本文件中的乱码 乱码是用文本编辑器(如笔记本程序或Word程序)打开源文件时,文本中部分字符是无法阅读或无法理解的一系列杂乱符号。在数据处理过程中,乱码问题让人头疼。 【例537】文本文件中的乱码现象如图515所示。如果文本内容全是乱码(见图515(a)),说明文件是二进制编码,或者编辑器不支持这种文本编码。如果文本中只有某几行出现乱码(见图515(b)),说明文本出现了编码错误,程序读取这种文本时非常容易出错,处理起来也非常麻烦。有些文本中含有不可见的控制符(见图515(c)、图515(d)),程序读取这些文本时也很容易出错。有些文件中含有HTML(Hyper Text Mark Language,超文本标记语言)标识符(见图515(e))和一些特殊符号(见图515(f)),这都需要编程处理。 图515文本中的乱码现象 2. 文本文件乱码原因 (1) 软件包原因。Python程序经常会用到第三方软件包,一些国外软件包可能采用单字节编码(如ISO 8859),这些软件包打开双字节语言(如GBK)文件时,如果不能正确识别文件分割符,就容易把一个汉字编码(2字节)从中分割为两段,这会导致紧接在后面的整个一行全部都是乱码(见图515(b))。 (2) 数据库原因。数据库字符编码与程序字符编码不一致,如数据库采用GBK编码,客户端程序采用UTF8编码,数据导出时就容易出现乱码(见图515(a))。 (3) 数据错误。例如,大部分网页都采用了JavaScript脚本程序,而JavaScript语言默认编码为ISO 8859。网络爬虫在爬取JSP网页数据后,如果存储为UTF8或GBK编码文件,以后Python程序读取这些文本时,就容易造成读写错误。 (4) 存储格式原因。例如,一些文本文件采用了字节保存模式,而Python程序采用文本读写操作时,就会出现读写错误。 3. 文本读错误的处理方法 (1) 开发环境设置。Python 3.x默认使用UTF8编码,因此程序开发和文本文件存储时应尽量选用UTF8编码,而不是GBK编码(Windows下为ANSI编码)。第一次使用IDE(如PyCharm)时,应将默认文本编辑器修改为UTF8编码。 (2) 对输入文本采用字节读的模式。 【例538】文本中夹杂有二进制字节码时,可对文本采用字节读(rb)模式。 1file = open('d:\\test\\05\\三国演义.txt', 'rb')# 采用二进制字节读模式 (3) 指明文本编码。 【例539】如果文本中有中文,可以在打开文件时指明文本编码模式。 1 file = open('d:\\test\\05\\三国演义.txt', 'rb', encoding='gbk')# 说明文本编码模式(GBK # 编码) (4) 用解码函数忽略非法字符。 【例540】利用函数decode()忽略文本中的非法字符。 1with open('d:\\test\\05\\春.txt', 'rb') as file:# 打开文本文件(绝对路径) 2data = file.read()# 读取文本文件 3txt = data.decode('GB2312', errors='ignore')# 参数errors='ignore'为忽略非法字符 4print(txt) (5) 设置常用编码集。 【例541】文件“射雕英雄传.txt”编码不明,尝试读出该文件中的20个关键字。 案例分析: 对某些不明编码的文本,可以在程序中设置多种编码集进行文本读,如设置UTF8、GB 18030、GBK、GB 2312编码集,必有一款编码适合读出文本。对文本文件的关键字提取可以采用“结巴分词”软件包(安装方法参见7.3.2节)。提高中文人名识别率的第三方软件包有LTP(中规中矩)、LAC(提取量少,但是正确率高)等。 1import jieba.analyse# 导入第三方包——结巴分词 2 3def read_from_file(directions): # 定义文本解码函数 4 decode_set = ['utf8', 'gb18030', 'gbk', 'gb2312', 'ISO-8859-2', 'Error'] # 定义编码集 5for k in decode_set:# 编码集循环 6try: 7file = open(directions, 'r', encoding = k) 8read_file = file.read() # 如果解码失败引发异常,就跳到except 9file.close() 10break # 如果文本打开成功,则跳出编码匹配 11except: 12if k=='Error': # 如果出现异常就终止程序运行 13raise Exception('射雕英雄传.txt文件无法解码!') # 出错提示信息 14continue 15return read_file 16file_data = str(read_from_file('d:\\test\\05\\射雕英雄传.txt'))# 读取文本文件 17tfidf = jieba.analyse.extract_tags(file_data, topK=20) # 提取关键字 18print('关键字:', set(tfidf)) >>># 程序输出 关键字: {'洪七公', '郭靖', '欧阳锋', '周伯通', '黄蓉', '武功', '爹爹', '说道', '郭靖', '黄蓉道', '欧阳克', '柯镇恶', '裘千仞', '丘处机', '师父', '梅超风', '黄药师', '功夫', '完颜洪烈', '两人'} 程序说明: 程序第4行,用多种编码读文本,常用编码放在前面,减少程序试错时间。由于GB 18030字符集较大,汉字覆盖较好,不容易出错,因此放在GBK编码前面。 习题5 51简要说明“文件句柄”的功能和在程序中的作用。 52简要说明函数read()与函数readlines()的相同和不同之处。 53open()语句或with open()语句都可以实现文件读写,它们有哪些差别? 54简要说明UTF8编码的特点。 55简要说明文本读错误的处理方法。 56编程: 统计“唐诗三百首.txt”文件中的字数和行数。 57编程: 数据文件test.txt内容如图516所示,把数据读入二维矩阵中。 58编程: 读取并打印“鸢尾花数据集.csv”。 59编程: 读取并打印“鸢尾花数据集.csv”数据集中第2行。 510编程: 将图517中数据保存到“成绩.csv”文件中。 图516数据集1 图517数据集2