Python随堂笔记 文件的读写

142 阅读12分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

🌴 2022.05.11 下午

前言

🎬本文章是 【Python语言基础】 专栏的文章,主要是上课的随堂笔记与练习 🔗Python专栏 传送门 💻提示:本文的环境配置是Python3的开发环境。目录顺序以当堂内容(教材)为准,所以并非“1、2、3”

9 文件的访问

9.1 概述

  • 文件:存储在外部存储器上的数据集合
  • 文件名:由基本名和扩展名组成,不同类型的文件扩展名也不相同

例如,文本文件的扩展名为.txt,Word文档的扩展名一般为.docx,可执行文件的扩展名一般为.exe,C语言源文件的扩展名为.c等

按文件内容分类

  • 程序文件:存储程序,包括源文件和可执行文件:如Python源文件(扩展名为.py)、C++源文件(扩展名为.cpp)、可执行文件(扩展名为.exe)等都是程序文件
  • 数据文件:存储程序运行所需要的各种数据: 如文本文件(.txt)、Word文档(.docx)、Excel工作簿(.xlsx)等

按信息存储形式分类

  • 文本文件:文本格式文件(.txt)、网页文件(.html)等
  • 二进制文件:常见的二进制文件有数据库文件、图像文件、可执行文件和音频文件等

9.2 文本文件访问

9.2.1 打开文件

在Python中,访问文本文件可以分为三步

  1. open()函数打开一个文件,返回一个文件对象
  2. 调用文件对象中的函数对文件内容进行读或写等操作
  3. 调用文件对象的close()函数关闭文件

🚀 open()

open()函数:open(filename[,mode]),w写,r读,a追加。其中w写会覆盖原来内容

file = open(r"d:\data1.txt","w")    		#打开一个文件用于写入
print("文件名字:",file.name)
print("访问模式:",file.mode)
print("是否已关闭:",file.closed)
file.close()                               #关闭文件
'''
文件名字: d:\data1.txt
访问模式: w
是否已关闭: False
'''

🚀 with-as

with open(filename[,mode]) as file:
            语句块

file为open()函数返回的文件对象

  • 简化程序代码
  • 自动释放和清理相关资源

使用with-as语句访问文本文件

with open("d:/data2.txt","w") as file:
  num = file.write("Go its own way, let others say!")    #向文件写入字符串
  print("写入字符 %d 个!"%num)
'''
写入字符 31 个!
'''

9.2.2 文件操作

🚀 写文件

  • file.write()函数:file.write(str)
  • file.writelines()函数:file.writelines(sequence),sequence为要写入的字符序列

使用file.write()函数向文件中写入字符串

with open("Shakespeare.txt","w") as file:	
  num = file.write( "黑夜无论怎样悠长\n白昼总会到来" )  	
  print("向文件中写入字符 %d 个!"%(num))   				
'''
向文件中写入字符 15 个!
'''

紧接着 打开上一段中的结果文件Shakespeare.txt,并向文件中追加新的内容

str = input("请输入内容:")
with open("Shakespeare.txt","a") as file:	      #以追加模式打开一个文件
  file.write("\n")   					#在文件末尾写入回车符
  num = file.write(str)   				#向文件中追加新的字符
  print("向文件中追加字符 %d 个!"%(num))
'''
请输入内容:—莎士比亚
向文件中追加字符 5 个!
'''

使用file.write()函数向文件中写入元组

mingTuple = ("王阳明","于谦","戚继光","海瑞","郑和","徐达") #明朝著名人物
with open("Ming.txt","w") as file:
  num = file.write(str(mingTuple)       #向文件中写入元组
  print("写入元组成功!")
'''
写入元组成功!
'''

Ming.txt文件内容:‘王阳明‘,‘于谦‘,‘戚继光‘,‘海瑞‘,‘郑和‘,‘徐达‘

使用file.writelines()函数向文件中写入字符序列

str = ["好雨知时节,当春乃发生。\n","随风潜入夜,润物细无声。"] 	
with open("杜甫.txt","w") as file:
  file.writelines(str)    								  
  print("写入字符序列成功!") 
'''
写入字符序列成功!
'''

好雨知时节,当春乃发生。 随风潜入夜,润物细无声。

🚀 读文件

  • file.read()函数:file.read([size]) ,当size被忽略或为负时,则该文件的所有内容都将被读取且返回,返回一个序列(即上一个例子好雨知时节中的str)
  • file.readline()函数:file.readline([size]) ,读取一行,size有参时返回的是前x个字符保存到列表中
  • file.readlines()函数:file.readlines() ,返回多行, file.readlines([sizeint])

使用file.read()函数读取Shakespeare.txt的内容

如果出现报错**'gbk' codec can't decode byte 0xa4 in position 4: illegal multibyte sequence**,with open("文件路径","模式",encoding='UTF-8'),加上编码格式

with open("Shakespeare.txt","r") as file:
  str = file.read() 						#读取文件中的内容
  print(str)
'''
黑夜无论怎样悠长
白昼总会到来
—莎士比亚
'''

使用for语句读取文件Shakespeare.txt的内容

with open("Shakespeare.txt","r") as file:
  for line in file:  # 循环读取文件中的内容
    print ( line, end='' )
'''
黑夜无论怎样悠长
白昼总会到来
—莎士比亚
'''

使用readline()函数读取Shakespeare.txt的一行内容

with open("Shakespeare.txt","r") as file:
  str = file.readline()   			#从文件中读取一行内容
  print(str)
'''
黑夜无论怎样悠长
'''

使用readlines()函数读取文件Shakespeare.txt的内容

with open("Shakespeare.txt","r") as f:
  str = f.readlines()    	#从文件中读取所有行,保存在列表
  print(str)				#print(str[0])输出第一行
'''
['黑夜无论怎样悠长\n', '白昼总会到来\n', '—莎士比亚']
'''

🚀 其它文件操作

  • file.close():关闭文件
  • file.flush():刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件
  • file.fileno():返回一个整型的文件描述符(file descriptor,FD,整型)
  • file.isatty():如果文件连接到一个终端设备则返回True,否则返回 False
  • file.tell():返回文件当前位置
  • file.seek():重定位文件对象的指针位置
  • file.truncate([size]):从文件的首行首字符开始截断,截断文件为size个字符,无 size 表示从当前位置截断;截断之后后面的所有字符被删除

使用函数file.tell()file.seek()重定位文件Shakespeare.txt

with open("Shakespeare.txt","r") as file:
  data = file.readline()
  print(data.strip())   					
  print('输出一行后的文件指针在:',file.tell())	#查看指针位置
  file.seek(0)    					#将文件指针重新定位在开始位置
  print('用seek()将文件指针放回开始处:',file.tell())
  print('再次输出:',file.readline())
'''
黑夜无论怎样悠长
输出一行后的文件指针在: 18		
用seek()将文件指针放回开始处: 0
再次输出: 黑夜无论怎样悠长
'''

一个字符连个字节,“黑夜无论怎样悠长\n",2*9=18

9.3 二进制文件访问

9.3.1使用Pickle模块读/写二进制文件

  • 序列化:是指把内存中的数据对象在不丢失其类型的情况下转换成对应的二进制信息的过程
  • 反序列化: 是指把序列化后的二进制信息准确无误地恢复到原来数据对象的过程

同文本文件访问过程相似,二进制文件访问也分为三个步骤

  1. 打开二进制文件
  2. 访问二进制文件
  3. 关闭二进制文件

在Python中访问二进制文件的常用模块有Pickle、Struct、Marshal和Shelve等

Pickle模块用于序列化和反序列化的函数有两个:pickle.dump()pickle.load()函数

  • pickle.dump()函数:pickle.dump(data,file[,protocol])
  • pickle.load()函数:pickle.load(file)

使用pickle.dump()函数向二进制文件中写入数据,注意模式是"wb",特指二进制

import pickle
list1 = [2,4,5,8]                      #数据
tuple1 = ("music","game","sports")     #数据
data = [list1,tuple1] 				   #数据
with open("pickle_file.dat","wb") as pickle_file:       #打开的二进制文件
  for i in data:
    pickle.dump(i,pickle_file)     			         #向文件中写入序列化内容
  print("写入数据成功!")
'''
写入数据成功!
'''

使用pickle.load()函数读取二进制文件中的内容,注意模式"rb"

import pickle
with open("pickle_file.dat","rb") as pickle_file:	
  data1 = pickle.load(pickle_file)    				
  print("data1:",data1)
  data2 = pickle.load(pickle_file)
  print("data2:",data2)
'''
data1: [2, 4, 5, 8]
data2: ('music', 'game', 'sports')
'''

9.3.2使用Struct模块读/写二进制文件

  • struct.pack(fmt,data1,data2, …):fmt为给定的格式化字符串。data1、data2为要写入的数据对象
  • struct.unpack(fmt, string):fmt为给定的格式化字符串。string为要反序列化的字符串

使用struct.pack()函数向二进制文件中写入数据

import struct
a = "Hello"
b = 35
c = 89.46
binary1 = struct.pack('5sif',a.encode("UTF-8"),b,c)
with open("struct_file.dat","wb") as struct_file:
  struct_file.write(binary1)   			#向文件中写入序列化内容.
print("写入数据成功!")
'''
写入数据成功!
'''

使用struct.unpack()函数读出二进制文件中的内容

import struct
with open("struct_file.dat","rb") as struct_file:
  binary1= struct_file.read()			#读出二进制文件中的内容
a,b,c = struct.unpack("5sif",binary1)  	#将读出的内容反序列化为数据对象
print("a:",a.decode("UTF-8"))
print("b:",b)
print("c: %.2f"%c)
'''
a: Hello
b: 35
c: 89.46
'''

9.3.3使用Marshal模块读/写二进制文件

  • marshal.dump()函数:marshal.dump(data,file),data为待序列化的数据对象。file为创建或打开的文件对象
  • marshal.load()函数:marshal.load(file),file为打开的二进制文件对象

使用marshal.dump()函数向二进制文件中写入数据

import marshal
#创建数据
data1 = ['def',34,87]  	#数据
data2 = {1:'优秀',2:'良好',3:'合格',4:'不合格'}    	                               #数据.
data3 = (5,6,7)         #数据
with open("marshal_file.dat",'wb') as output_file:
  marshal.dump(data1,output_file)  				#向文件中写入序列化内容
  marshal.dump(data2,output_file)  				#向文件中写入序列化内容
  marshal.dump(data3,output_file)  				#向文件中写入序列化内容
  print("写入数据成功!")
'''
print("写入数据成功!")
'''

使用marshal.load()函数读出二进制文件中的内容

import marshal
with open('marshal_file.dat','rb') as marshal_file:    #打开二进制文件
  data1 = marshal.load(marshal_file)           	#将读出的内容反序列化为数据对象
  data2 = marshal.load(marshal_file)           	#将读出的内容反序列化为数据对象
  data3 = marshal.load(marshal_file)           	#将读出的内容反序列化为数据对象
  print(data1)
  print(data2)
  print(data3)
'''
['def', 34, 87]
{1: '优秀', 2: '良好', 3: '合格', 4: '不合格'}
(5, 6, 7)
'''

9.3.4使用Shelve模块读/写二进制文件

Shelve模块提供一种类似字典的方式操作二进制文件的功能,Shelve提供一个简单的数据存储方案,只有一个open()函数,open()函数接收一个文件名作为参数,返回一个shelf对象,格式: shelve.open(filename, mode, protocol=None, writeback=False)

  • filename:打开或创建的二进制文件
  • mode:文件打开模式
  • protocol:序列化模式,可以是1或2,默认值为None
  • writeback:是否将所有从二进制文件中读取的对象存放到一个内存缓存

使用Shelve模块向二进制文件中写入数据

import shelve
tg = dict(zip(['name','age'],['Tong Gang',31]))     
tj = dict(zip(['name','age'],['Tie Jin',42]))       
with shelve.open('shelve_file') as shelve_file:
  shelve_file['tg'] = tg                        	#向文件中写入序列化内容
  shelve_file['tj'] = tj                        	#向文件中写入序列化内容
  print("写入数据成功!")
'''
写入数据成功!
'''

使用Shelve模块读出二进制文件中的内容

import shelve
with shelve.open('shelve_file') as shelve_file:
  print(shelve_file['tg'])                 		#读数据
  print(shelve_file.get('tj'))            		#读数据
'''
{'name': 'Tong Gang', 'age': 31}
{'name': 'Tie Jin', 'age': 42}
'''

使用Shelve模块修改二进制文件中的内容

import shelve
with shelve.open('shelve_file') as shelve_file:
  tg = shelve_file['tg']                  	#从文件中读取数据
  tg['name'] = 'Tong Gen'             #修改数据
  shelve_file['tg'] = tg                  	#存储数据
  print(shelve_file ['tg'])
'''
{'name': 'Tong Gen', 'age': 31}
'''

9.4 典型案例

9.4.1 合并文件

合并指定路径下的联系人文件,要求没有重复的联系人信息

分析:在指定路径下有3个联系人文件contacts01.txt、contacts02.txt和contacts03.txt,3个联系人文件中的联系人信息有重复之处,需要合并,方法如下

  1. 获取并创建指定路径下的联系人文件列表fileList
  2. 创建保存联系人的联系人信息列表tempList和联系人信息文件contacts.txt
  3. 对fileList中的文件file进行遍历:如果file中指定行的联系人信息不在tempList中,则说明联系人信息没有重复,添加到tempList中,写入文件contacts.txt中;否则,不添加到tempList中,也不写入文件contacts.txt中
  4. 如果发生异常,则进行处理,并给出提示信息
import os,sys,re
try:
  #获取目标文件夹的路径
  source_file_dir = os.getcwd() + '\\SourceFiles'
  #获取当前文件夹中的文件名称列表
  fileList = os.listdir(source_file_dir)
  #如果指定路径下没有需要合并的联系人文件,则退出程序
  if not fileList:
    print("指定路径下没有需要合并的联系人文件.")
    sys.exit(0)                             	#退出程序
 #打开当前目录下的contacts.txt文件,如果没有则创建
  with open(source_file_dir + '\\contacts.txt','w')  as file:
    tempList = []                           	#创建联系人信息列表
    #遍历文件列表中的联系人文件
    for fileName in fileList:
      filepath = source_file_dir + '\\' + fileName
      #遍历单个文件,读取1行
      for line in open(filepath):
        line = re.sub('\n','',line)      	#删除联系人信息末尾的'\n'
        #如果在line中而不在tempList中,则line没有重复,添加到tempList中,并写入file中
        if line not in tempList and  line + '\n' not in tempList:
          tempList.append(line)           	#将1行数据添加到tempList
          file.write(line)                 	#将1行数据写入file中
          file.write("\n")
    print("合并联系人文件成功!")
except IOError as e:                      	             #指定路径不存在或读/写异常
    print("异常信息:",e.args[1],e.filename)
except:
  print("合并联系人文件失败!")
'''
合并联系人文件成功!
'''

9.4.2 CSV文件操作

  • CSV (Comma Separated Values,逗号分隔值)文件是一种常用的文本格式文件,用以存储表格数据,包括数字、字符等
  • 很多程序在处理数据时都会使用CSV文件,其使用非常广泛
  • Python内置了Csv模块来处理CSV文件
  • Csv模块中用于文件处理的常用函数如下
    1. csv.writer(csvfile):返回一个writer对象。csvFile为打开的CSV文件对象
    2. writer.writerow(row):向CSV文件中写入一行数据
    3. writer.writerows(rows):向CSV文件中一次写入多行数据
    4. csv.reader(csvFile):返回一个reader对象,可通过迭代读取其中的内容。csvFile为打开的CSV文件对象

【例】 对CSV文件进行读/写操作

  1. 打开(或创建)salary.csv文件,通过Csv模块中的csv.writer()函数将返回的文件对象file转换为writer对象fw
  2. 调用fw.writerow()函数向CSV文件中写数据(包括标题和行数据)
  3. 打开salary.csv文件,通过Csv模块中的csv.reader()函数将返回的文件对象file转换为reader对象fr
  4. 从reader对象fr中读取数据
程序代码:
import csv
#标题.
headers = ['NO','Name','Sex','Age','Salary']
#多行数据
rows = [('1001','Jack','male',28,12800),('1002','Rose','female',22,8800),                   ('1003','Tim','male','26',10800)]
#向CSV文件中写数据
try:
  #以写模式打开文件.
  with open('salary.csv','w',newline='') as file:
    fw = csv.writer(file)                         	                #返回writer对象
    fw.writerow(headers)                          	#向CSV文件中写入一行数据
    fw.writerows(rows)                             	#向CSV文件中写入多行数据
    print('写入数据成功!')
except:
  print('写入数据失败!')

#从CSV文件中读数据.
print('读取数据结果:')
try:
  #以读模式打开文件.
  with open('salary.csv','r') as file:
    fr = csv.reader(file)                       #返回reader对象
    rows = [row for row in fr]                 	#以列表形式返回fr中的数据
    print("标题:",rows[0])   
    print("第1行数据:",rows[1]) 
    column = [row[1] for row in rows]
    print("第1列数据:",column)
    print("第1行第1列数据:", rows[1][1])
except:
    print('读取数据失败!')
'''
 写入数据成功!
 读取数据结果:
 标题: ['NO', 'Name', 'Sex', 'Age', 'Salary']
 第1行数据: ['1001', 'Jack', 'male', '28', '12800']
 第1列数据: ['Name', 'Jack', 'Rose', 'Tim']
 第1行第1列数据: Jack
'''