第九章 文件操作
在本章节, 主要介绍了文件操作相关的API方法使用 首先我们将会学习什么是文件操作, 以及文件分类还有在IO操作时会用到的常用编码介绍 然后我们学习了文件操作的流程, 创建->写入->关闭 在然后我们学习了文件的拓展, 序列化模块pickle, 文件操作模块csv, 系统操作调用模块os和os.path以及文件拷贝压缩模块shutil
一个完整的程序一般都包括数据的存储和读取;我们在前面写的程序数据都没有进行实际的存储,因此python解释器执行完数据就消失了 实际开发中,我们经常需要从外部存储介质(硬盘、光盘、U盘等)读取数据,或者将程序产生的数据存储到文件中,实现“持久化”保存
按文件中数据组织形式,我们把文件分为文本文件和二进制文件两大类:
在操作文本文件时,经常会操作中文,这时候就经常会碰到乱码问题. 为了解决中文乱码问题,需要学习下各个编码之前的问题.
常用编码之间的关系如下:
全称为 American Standard Code for Information Interchange ,美国信息交换标准代码, 这是世界上最早最通用的单字节编码系统,主要用来显示现代英语及其他西欧语言
注意事项:
2^7=128
个字符,用7bit即可完全编码,
而一字节8bit的容量是256,所以一字节 ASCII 的编码最高位总是0GBK即汉字内码扩展规范,英文全称Chinese Internal Code Specification. GBK编码标准兼容GB2312,共收录汉字21003个、符号883个,并提供1894个造字码位,简、繁体字融于一库。 GBK采用双字节表示,总体编码范围为8140-FEFE,首字节在81-FE 之间,尾字节在40-FE 之间
Unicode 编码设计成了固定两个字节,所有的字符都用16位**
2^16=65536
**表示,包括之前只占8位的英文字符等,所以会造成空间的浪费 Unicode 完全重新设计,不兼容 iso8859-1 ,也不兼容任何其他编码
对于英文字母, unicode 也需要两个字节来表示, 所以 unicode 不便于传输和存储. 因此而产生了 UTF编码 , UTF-8 全称是( 8-bit UnicodeTransformation Format )
注意事项
open() 函数用于创建文件对象,基本语法格式如下: open(文件名[,打开方式])
注意:
D:\a\b.txt
可以使用原始字符串 r“d:\b.txt”
减少 \
的输入, 因此以上代码可改写成 f = open(r"d:\b.txt","w")
文本文件的写入一般就是三个步骤:
实操代码
# 1.使用open()方式
f = open(r"d:\a.txt", "a")
s = "TimePause\n时间静止\n"
f.write(s)
f.close()
结果展示
windows 操作系统默认的编码是 GBK , Linux 操作系统默认的编码是 UTF- 8 . 当我们用 open() 时,调用的是操作系统相关api来打开的文件,并且默认的编码是 GBK 但是由于我们通常习惯将所有代码编码都设置成 UTF- 8 ., 因此在打开时会出现乱码问题, 如下图所示
解决方案:
按照上图提示, 将文本编码设置成 GBK格式读取即可
注意:
我们还可以通过指定编码来解决中文乱码问题. 因为我们将pycharm
文本读写编码都设置成 utf-8,
因此只要我们在文件写入的时候规定编码为 utf-8(默认gbk), 那么我们在读取时就不会出现乱码. 如下代码
实操代码
# 【示例】通过指定文件编码解决中文乱码问题
f = open(r"d:\bb.txt", "w", encoding="utf-8")
f.write("一个有温情的小站\n时间静止不是简史")
f.close()
问题描述
我们一般习惯把pycharm所有字符编码设置成utf-8时. 在我们进行网络请求时, 有时候会返回乱码问题, 如下图
问题分析
因为我们在 pycharm 设置所有字符编码均为 UTF-8, 但是通过网络请求得到GBK格式的文本, 然后我们仍以 UTF-8 编码去解码就会出现乱码
解决方案
可以将项目编码设置成GBK格式即可; 也可以通过文本操作代码对得到的数据进行GBK格式读取 亦或者在写入时, 直接将编码声明为UTF-8
write(a)
:把字符串 a 写入到文件中writelines(b)
:把字符串列表写入文件中,不添加换行符实操代码
# 【操作】添加字符串列表数据到文件中
f = open(r"d:\bb.txt", 'w', encoding="utf-8")
s = ["什么鬼\n"] * 3 # 通过 \n实现手动换行
f.writelines(s)
f.close()
由于文件底层是由操作系统控制,所以我们打开的文件对象必须显式调用 close() 方法关闭文件对象. 当调用 close() 方法时,首先会把缓冲区数据写入文件(也可以直接调用 flush() 方法),再关闭文件,释放文件对象
注意:
close()
一般结合异常机制的 finally 一起使用实操代码
# 【操作】结合异常机制的 finally ,确保关闭文件对象
# "a" 设置打开方式为追加模式
try:
f = open(r"d:\c.txt", "a")
s = "来自深渊"
f.write(s)
except BaseException as e:
print(e)
finally:
f.close()
with关键字 (上下文管理器)可以自动管理上下文资源,不论什么原因跳出 with块 ,都能确保文件正确的关闭, 并且可以在代码块执行完毕后自动还原进入该代码块时的现场
实操代码
# 【操作】使用 with 管理文件写入操作
s = ["齐格飞"] * 3
with open(r"d:\cc.txt", "w") as f:
f.writelines(s)
文件读取的步骤:
文件的读取一般使用如下三个方法:
read([size])
: 从文件中读取 size 个字符,并作为结果返回
如果没有 size 参数,则读取整个文件. 读取到文件末尾,会返回空字符串readline()
**: 读取一行内容作为结果返回**
读取到文件末尾,会返回空字符串readlines()
: 文本文件中,每一行作为一个字符串存入列表中,返回该列表代码格式
with open(r"d:\a.txt", "r"[, encoding="utf-8"]) as f:
f.read(4)
注意:
encoding="utf-8"
,
则控制台报 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xbc in position 13: invalid start byte
实操代码
# 【操作】 读取一个文件前4个字符
import pickle
with open(r"d:\a.txt", "r") as f:
print(f.read(4))
# 【操作】文件较小,一次将文件内容读入到程序中
with open(r"d:\aa.txt", "r") as f:
print(f.read())
# 【操作】按行读取一个文件
with open(r"d:\b.txt") as f:
while True:
lines = f.readline()
if not lines: # 在python 中 if not 会将后面对象隐式转成True或者False进行判断, 因此遇到空字符串也返回False
break
else:
print(lines, end="")
print()
# 【操作】使用迭代器(每次返回一行)读取文本文件
# 写和读的编码要对应
with open(r"d:\bb.txt", "r", encoding="utf-8") as f:
for a in f:
print(a, end="")
# 【操作】为文本文件每一行的末尾增加行号
with open(r"d:\c.txt", "r") as f:
lines = f.readlines()
lines2 = [line.rstrip() + " # " + str(index) + "\n" for index, line in zip(range(1, len(lines) + 1), lines)]
with open(r"d:\c.txt", "w") as ff:
ff.writelines(lines2)
二进制文件的处理流程和文本文件流程一致。首先还是要创建文件对象, 创建好二进制文件对象后,仍然可以使用 write() 、 read() 实现文件的读写操作
在创建文件对象时, 首先需要指定二进制模式,然后才能创建出二进制文件对象. 例如
f = open(r"d:\a.txt", 'wb')
可写的、重写模式的二进制文件对象f = open(r"d:\a.txt", 'ab')
可写的、追加模式的二进制文件对象f = open(r"d:\a.txt", 'rb')
可读的二进制文件对象实操代码
# 二进制文件的读取和写入(此操作相当于复制)
# f = open(r"d:\a.txt", 'wb') #可写的、重写模式的二进制文件对象
# f = open(r"d:\a.txt", 'ab') #可写的、追加模式的二进制文件对象
# f = open(r"d:\a.txt", 'rb') #可读的二进制文件对象
with open(r"d:\aaa.png", "rb") as scrFile, open(r"d:\bbb.png", "wb") as destFile:
for l in scrFile:
destFile.write(l)
文件对象的属性
文件对象的打开模式
文件对象的常用方法
利用 seek() 可以将读取文件的指针移动到指定字节位置上 一个中文字符站两个字节, 而英文只占一个字节
实操代码
print("=================文件任意位置操作======================")
# 【示例】 seek() 移动文件指针示例
with open(r"d:\cc.txt", "r") as f:
print("文件名是{0}".format(f.name)) # 文件名是d:\cc.txt
print(f.tell()) # 0
print("读取文件的内容", str(f.readline())) # 读取文件的内容 齐格飞齐格飞齐格飞
print(f.tell()) # 18
f.seek(4, 0) # 中文占2个字节, 因此在seek时需要是2的倍数
print("文件读取的内容", str(f.readline())) # 文件读取的内容 飞齐格飞齐格飞
print(f.tell()) # 18
序列化指的是:将对象转化成“串行化”数据形式,存储到硬盘或通过网络传输到其他地方. 反序列化是指相反的过程,将读取到的“串行化数据”转化成对象 可以使用pickle模块中的函数,实现序列化和反序列操作
序列化我们使用:
pickle.dump(obj, file)
obj 就是要被序列化的对象, file 指的是存储的文件pickle.load(file)
从 file 读取数据,反序列化成对象实操代码
import pickle
print("=================使用pickle序列化=======================")
# 【操作】将对象序列化到文件中
with open("student.info", "wb") as f:
name = "时间静止"
age = 18
score = [90, 80, 70]
resume = {"name": name, "age": age, "score": score}
pickle.dump(resume, f)
# 【操作】将获得的数据反序列化成对象
with open("student.info", "rb") as f:
resume = pickle.load(f)
print(resume)
csv是逗号分隔符文本格式,常用于数据交换、Excel文件和数据库数据的导入和导出
与Excel文件不同,CSV文件中:
Python标准库的模块csv提供了读取和写入csv格式文件的对象
我们在excel中建立一个简单的表格并且另存为 csv(逗号分隔) ,我们打开查看这个csv文件内容
实操代码
import csv
with open(r"d:\workBook.csv") as a:
o_csv = csv.reader(a) # #创建csv对象,它是一个包含所有数据的列表,每一行为一个元素
headers = next(o_csv) # #获得列表对象,包含标题行的信息
print(headers)
for row in o_csv: # 循环打印各行内容
print(row)
结果展示
实操代码
# 【操作】 csv.writer 对象写一个csv文件
headers = ['姓名', '年龄', '工作', '住址']
rows = [('JOJO', '18', '按摩师', '英国'), ('迪奥', '19', '老板', '埃及'), ('乔鲁诺乔巴纳', '20', '混混', '意呆利')]
with open(r"d:\workBook3.csv", "w") as b:
b_scv = csv.writer(b) # 创建csv对象
b_scv.writerow(headers) # 写入一行(标题)
b_scv.writerows(rows) # 写入多行(数据)
结果展示
os模块 可以帮助我们直接对操作系统进行操作. 我们可以直接调用操作系统的可执行文件、命令,直接操作文件、目录等等 os模块 是做系统运维非常重要的基础
实操代码
# 【示例】 os.system 调用windows系统的记事本程序
os.system("notepad.exe")
# 【示例】 os.system 调用windows系统中ping命令
# 如果出现乱码, 请看 文件操作->文件的写入->中文乱码->控制台输出时 进行配置
os.system("ping www.baidu.com")
# 【示例】运行安装好的微信
os.startfile(r"C:\Program Files (x86)\Tencent\WeChat\WeChat.exe")
可以通过前面讲的文件对象实现对于文件内容的读写操作. 如果还需要对文件和目录做其他操作,可以使用 os 和 os.path 模块.
实操代码
import os
# 【示例】 os 模块:创建、删除目录、获取文件信息等
print("系统名称:", os.name) # windows-->nt linux-->posix
print("当前操作系统所使用的路径分隔符:", os.sep) # windows-->\ linux-->/
print("行间隔符:", repr(os.linesep)) # windows-->\r\n linux-->\n
print("当前目录:", os.curdir)
a = "3"
print(a)
# 返回对象的规范字符串表示
print(repr(a))
# 获取文件和文件夹的相关信息
print(os.stat("MyPy08-FileRead.py"))
# 工作目录的操作
print(os.getcwd()) # 获取当前工作目录
os.chdir("D:") # 切换当前工作目录
os.mkdir("学习资料大全") # 创建目录
os.rmdir("学习资料大全") # 删除目录
# os.makedirs("人种/黄种人/中国人") # 创建多级目录, 调用成功一次之后, 再次调用会报错
# os.rename("人种", "亚洲人") # 此方法也只能调用一次
print(os.listdir("亚洲人")) # 当前目录下的子目录
注意事项
在调用 os.rename()
时, 如果出现报错 PermissionError: [WinError 5] 拒绝访问
,
则需要你在需要重命名的文件夹上面配置用户的权限. 修改之后便可进行重命名. 如下图所示
os.path 模块提供了目录相关(路径判断、路径切分、路径连接、文件夹遍历)的操作
实操代码
# 【示例】测试 os.path 中常用方法
print("是否是绝对路径:", os.path.isabs("d:/a.txt"))
print("是否是目录: ", os.path.isdir(r"d:\a.txt"))
print("文件是否存在: ", os.path.exists("a.txt"))
print("文件大小: ", os.path.getsize("a.txt"))
print("输出绝对路径:", os.path.abspath("a.txt"))
print("输出所在目录:", os.path.dirname("d:/a.txt"))
# 获得创建时间、访问时间、最后修改时间
print("输出创建时间:", os.path.getctime("a.txt"))
print("输出最后访问时间:", os.path.getatime("a.txt"))
print("输出最后修改时间", os.path.getmtime("a.txt"))
# 对路径进行分割、连接操作
path = os.path.abspath("a.txt") # 返回绝对路径
print("返回元组:目录、文件:", os.path.split(path))
print("返回元组:路径、扩展名", os.path.splitext(path))
print("返回路径:aa\bb\cc", os.path.join("aa", "bb", "cc"))
列出指定目录下所有的 .py 文件,并输出文件名
# 列出指定目录下所有的 .py 文件,并输出文件名
import os
path = os.getcwd()
file_list = os.listdir(path)
for filename in file_list:
pos = filename.rfind(".")
if filename[pos + 1:] == "py":
print(filename, end="\t")
print()
os.walk() 方法是一个简单易用的文件、目录遍历器,可以帮助我们高效的处理文件、目录方面的事情
格式如下: os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]])
实操代码
# 【示例】使用 walk() 递归遍历所有文件和目录
path = os.getcwd()[:os.getcwd().rfind("\\")] # 获取上级路径, 作用是能够输出更多的文件
file_list = os.walk(path, topdown=False)
for root, dirs, files in file_list:
for name in files:
print(os.path.join(root, name))
for name in dirs:
print(os.path.join(root, name)) # 用于拼接目录
输出结果
实操代码
# 【示例】使用递归算法遍历目录下所有文件
def my_print_file(path, level):
child_files = os.listdir(path)
for file in child_files:
file_path = os.path.join(path, file)
print("\t" * level + file_path[file_path.rfind(os.sep)+1:])
if os.path.isdir(file_path):
my_print_file(file_path, level + 1)
my_print_file(path, 0)
shutil 模块是python标准库中提供的,主要用来做文件和文件夹的拷贝、移动、删除等; 还可以做文件和文件夹的压缩、解压缩操作. os 模块提供了对目录或文件的一般操作. shutil 模块作为补充,提供了移动、复制、压缩、解压等操作,这些 os 模块都没有提供
实操代码-拷贝
import shutil
# 【示例】实现文件的拷贝
os.chdir("D:") # 切换当前工作目录
shutil.copyfile("a.txt", "a_copy.txt")
# 【示例】实现递归的拷贝文件夹内容(使用 shutil 模块)
shutil.copytree("亚洲人/黄种人", "人种", ignore=shutil.ignore_patterns("*.html", "*htm")) # "音乐"文件夹不存在才能用
实操代码-压缩与解压
# 【示例】实现将文件夹所有内容压缩(使用 shutil 模块)
# 将"亚洲人/黄种人"文件夹下所有内容压缩到"生物资料"文件夹下生成race.zip
shutil.make_archive("生物资料/race", "zip", "亚洲人/黄种人")
# 压缩:将指定的多个文件压缩到一个zip文件
z = zipfile.ZipFile("a.zip", "w")
z.write("a.txt")
z.write("b.txt")
z.close()
# 【示例】实现将压缩包解压缩到指定文件夹(使用 shutil 模块)
z2 = zipfile.ZipFile("a.zip", "r")
z2.extractall("d:/生物资料")
z2.close()