文件操作(IO技术)
一个完整的程序一般都包括数据的存储和读取。实际开发中,一般都需要从外部存储介质(硬盘、光盘、U盘 等)读取数据,或者将程序产生的数据存储到文件中,实现持久化保存。
很多软件是将数据存储在数据库中,数据库实际也是基于文件形式存储的。
文本文件和二进制文件
按文件中数据组织形式,我们把文件分为文本文件和二进制文件两大类。
- 文本文件
文本文件存储的是普通“字符”文本,默认为Unicode字符集,可以用记事本打开。但是,像word软件编辑文档不是文本文件。
- 二进制文件
二进制文件把数据内容用“字节”进行存储,无法用记事本打开,必须使用专用的软件解码,常见的有:MP4视频文件、MP3音频文件、JPG图片、doc文档等等。
文件操作相关模块概述
python标准库中,如下是文件操作相关的模块。
名称 | 说明 |
---|---|
io模块 | 文字流的输入和输出操作 |
os模块 | 基本操作系统功能,包括文件操作 |
glob模块 | 查找符合特定规则的文件路径名 |
fnmatch模块 | 使用模块来匹配文件路径名 |
fileinput模块 | 处理多个输入文件 |
filecmp | 用于文件比较 |
cvs模块 | 用于csv文件比较 |
pickle和cpickle | 用于序列化和反序列化 |
xml包 | 用于xml数据处理 |
bz2、gzip、zipfile、tarfile | 用于处理压缩和解压缩文件(分别对应不同算法) |
创建文件对象open()
open()函数用于创建文件对象,基本语法格式如下:
open(文件名[,打开方式])
如果只是文件名,代表在当前目录下的文件。文件可以录入全路径,比如:D:\a\b\b.text。为了减少“\”的输入,可以使用原字符串:r“d:\b.text”。示例如下:
f=open(r"d:\b.text","a")
打开方式有如下几种:
模式 | 描述 |
---|---|
r | 读模式 |
w | 写模式。如果文件不存在则创建;如果文件存在,则重写新内容 |
a | 追加模式。如果文件不存在则创建;如果文件存在,则在文件末尾追加新内容 |
b | 二进制模式(可以其他模式组合使用) |
+ | 读、写模式(可与其他模式组合使用) |
文本文件对象和二进制对象的创建:
如果我们没有增加模式“b”,则默认创建的是文本文件对象,处理的基本单元是“字符”。如果二进制模式“b”,则创建的是二进制文件对象,处理的基本单元是“字节”
注:
在打开文件的时候open(r’c:.…’)加r和不加’’r是有区别的
‘r’是防止字符转义的 如果路径中出现’\t’的话,不加r的话\t就会被转义,而加了’r’之后’\t’就能保留原有的样子
文本文件的写入
基本的文件写入操作
文本文件的写入一般就是三个步骤:
- 创建文件对象
- 写入数据
- 关闭文件对象
write()/writelines()写入数据
write(a):把字符串a写入到文件中
writelines(b):把字符串列表写入到文件中,不添加换行符
文本文件的读取
文本文件读取一般使用如下三个方法:
- read([size])
从文件中读取size个字符,并作为返回结果、如果没有size参数,则读取整个文件。读取到文件末尾,会返回空字符串。
- readline()
读取一行内容作为返回结果。读取到文件末尾就会返回空字符串
- readlines()
文本文件中,每一行作为一个字符串存入到列表中,返回该列表
with open(r"d.txt",encoding="utf-8") as f:
for a in f:
print(a,end='')
with open(r"d.txt","r",encoding="utf-8") as f:
while True:
fragment = f.readline()
if not fragment:
break
else:
print(fragment,end="")
常见的编码介绍
在操作文本文件时,经常会操作中文,这时候就会碰到乱码问题。下面为各种编码之间的关系
ASCII
全称为American Standard Code for Information Interchange,美国信息交换标准代码,这是世界上最早通用的单字节编码系统,主要用来显示现代英语及其他西欧语言。
ASCII码用7位表示,只能表示128个字符。只定义了27=128个字符,用7bit即可完全编码,而1字节8bit的容量是256,所以1字节ASCII的最高的编码总是0。
0~31表示控制字符如回车、退格、删除等;32~126表示打印字符,即可以通过键盘输入并且能显示出来的字符;48~56为0到9十个阿拉伯数字,65~90位26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符等。
ISO8859-1
ISO8859-1又称为Latin-1,是一个8位单字节字符集,它把ASCII的最高位也利用起来,并兼容了ASCII,新增的空间是128,但他并没有完全用完。
在ASCII编码之上又增加了西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号,它是向下兼容ASCII编码。
GB2312,GBK,GB18030
- GB2312
GB2312全称为信息交换用汉字编码字符集,是中国1980年发布,主要用于计算机系统中的汉字处理。GB2312主要收录了6763个汉字、682个符号。GB2312覆盖了汉字大部分使用率,但不能想古汉语等特殊的罕用字,所以后来出现了像GBK、GB18030这种编码。GB2312完全兼容ISO8859-1。
- GBK
全称为Chinese Internal Code Specification,即汉字内码扩展规范,用于1995年制定。它主要是扩展GB2312,在它的基础上又增加了更多的汉字,它一共收录了21003个汉字。
- GB18030
现在最新的内码字集于2000发布,并于2001年强制执行,包含中国大部分少数民族语言字符,收录汉字超过70000余个。
Unicode
Unicode编码设计成了固定两个字节,所有的字符都用16位(2^16=65536)表示,包括之前只占8位的英文字符等,所以会造成空间的浪费,Unicode在很长的一段时间内都没有得到推广应用。
Unicode完全重新设计,不兼容ISO8859-1,也不兼容其他编码。
UTF-8
对于英文字母,Unicode也需要两个字节表示。所以Unicode不便于传输和存储。因此而产生了UTF-8全称是(8-bit Unicode Transformation Format)
UTF编码兼容ISO8859-1编码,同时也用来表示所有语言的字符,不过,UTF编码都是不定长编码,每一个字符的长度从1~4个字节不等。其中英文字母都是用一个字节表示,而汉字用三个字节
一般项目都会使用UTF-8。Unicode中虽然汉字是两个字节,UTF-8中汉字是3个字节。但是互联网中一个网页也包含了大量的英文字母,这些英文字母只占用1个字节,整体占用空间。UTF-8仍然由于Unicode、
中文乱码问题
windows操作系统系统默认的编码是GBK,Linux操作系统默认的编码是UTF-8。当我们用open()时,调用的是操作系统打开的文件,默认的编码是GBK
close()关闭文件流
由于文件底层是由操作系统控制,所以我们打开的文件对象必须显示调用close()方法关闭文件对象。当调用close()方法时,首先会把缓冲区数据写入文件(也可以直接调用flush()方法,再关闭文件,释放文件对象。
为了确保文件对象正常关闭,一般结合异常机制的finally或者with关键字实现无论何种情况都能关闭打开的文件对象。
try:
f = open(r"a.txt","a")
str = "abc"
f.write(str)
except BaseException as e:
print(e)
finally:
f.close()
with语句(上下文管理器)
with关键字(上下文管理文件写入操作)可以自动管理上下文资源,无论什么原因跳出with块,都能确保文件正确的关闭,并且可以在代码块执行完毕后自动还原进入该代码块时的现场。
with open(r"c.txt","a") as f:
f.write("abc")
enumerate()函数
a = ["你\n","你好\n","你好啊\n"]
b = enumerate(a)
print(a)
print(list(b))
c = [temp.rstrip()+"#"+str(index) for index,temp in enumerate(a)]
#rstrip()去除空白符\n
#temp 临时变量你,你好,你好啊
#index #0 #1 #2
print(c)
with open(r"d.txt","r",encoding="utf-8") as f:
lines = f.readlines()
lines = [line.rstrip()+"#"+str(index+1)+"\n" for index,line in enumerate(lines)]
#推导式
with open(r"d.txt","w",encoding="utf-8") as f:
f.writelines(lines)
二进制文件的读取和写入
二进制文件的处理流程和文本文件流程一致。首先我们还是要创建文件对象,不过,我们需要指定二进制模式,从而创建出二进制文件对象。例如:
f = open(r"d:\a.txt","wb") #可写的、重写模式二进制对象
f = open(r"d:\a.txt","ab") #可写的、追加模式的二进制对象
f = open(r"d:\a.txt","rb") #可读的二进制文件对象
创建好二进制文件对象后,仍然可以使用write()、read()实现文件的读写操作
with open(r"picture1.png","rb") as f:
with open(r"picture2.png","wb") as w:
for line in f.readlines():
w.write(line)
print("图片拷贝完成")
文件对象的常用属性和方法
文件对象封装了文件相关的操作,下面是文件对象的常用属性和方法:
文件对象的属性
属性 | 说明 |
---|---|
name | 返回文件的名字 |
mode | 返回文件的打开模式 |
close | 若文件被关闭则返回True |
文件对象的打开模式
模式 | 描述 |
---|---|
r | 读模式 |
w | 写模式。如果文件不存在则创建;如果文件存在,则重写新内容 |
a | 追加模式。如果文件不存在则创建;如果文件存在,则在文件末尾追加新内容 |
b | 二进制模式(可以其他模式组合使用) |
+ | 读、写模式(可与其他模式组合使用) |
文件对象的常用方法
方法名 | 说明 |
---|---|
read([size]) | 从文件中读取size个字节或者字符的内容返回。 若省略[size],则读取搭配文件末尾,即一次读取文件所有内容 |
readline() | 从文本文件中读取一行内容 |
readlines() | 把文本文件中每一行都作为独立的字符串对象,并且将这些对象放入列表返回 |
wirte(str) | 将字符串str内容写入文件 |
writelines(s) | 将字符串列表s写入文本文件,不添加换行符 |
seek(offset[,whence]) | 把文件指针移动到新的位置,offset表示相当于whence的多少个字节的偏移量; offset: off为正往结束方向移动,为负往开始方向移动 whence不同的值代表不同含义: 0:从文件头开始计算(默认值) 1:从当前位置开始计算 2:从文件尾开始计算 |
tell | 返回文件指针的当前位置 |
truncate([size]) | 无论指针在什么位置,只留下指针前size个字节内容,其余全部删除;如果没有传入size,则当指针当前位置到文件末尾内内容全部删除 |
flush() | 把缓存区的内容写入文件,但不关闭文件 |
close() | 把缓冲区的内容写入文件,同时关闭文件,释放文件对象相关资源 |
seek()
with open(r"d.txt","r",encoding="utf-8") as f:
print("文件名为:{0}".format(f.name))
print(f.tell())
print("读取的内容:{0}".format(str(f.readline())))
print(f.tell())
f.seek(3)
print("读取的内容:{0}".format(str(f.readline())))
使用pickle序列化
python中,一切皆对象,对象本质上就是一个“存储数据的内存块”有时候,我们需要将“内存块的数据”保存到硬盘上,或者通过网络传输到其他计算机上,这时候,就需要“对象的序列化和反序列化”。对象的序列化机制广泛的应用在分布式、并行系统上。
序列化指的是:将对象转化成“串行化”数据形式,存储到硬盘或者网络传输到其他地方。反序列化是指相反的过程,将读取到的“串行化数据”转化成对象<br /> <br />我们可以使用pickle模块的函数,实现序列化和反序列化操作
序列化我们使用:
pickle.dump(obj,file) obj就是要被序列化的对象,file指的是存储的文件
pickle.load(file) 从file读取数据,反序列化成对象
import pickle
with open(r"date.dat","wb") as f:
a1 = "abc"
a2 = "123"
a3 = [20,30,40]
pickle.dump(a1,f)
pickle.dump(a2,f)
pickle.dump(a3,f)
import pickle
with open(r"date.dat","rb") as f:
a1 = pickle.load(f)
a2 = pickle.load(f)
a3 = pickle.load(f)
print(a1)
print(a2)
print(a3)
import pickle
a1 = "abc"
a2 = "123"
a3 = [20,30,40]
with open(r"date.dat","wb") as f:
pickle.dump(a1,f)
pickle.dump(a2,f)
pickle.dump(a3,f)
with open(r"date.dat","rb") as f:
b1 = pickle.load(f)
b2 = pickle.load(f)
b3 = pickle.load(f)
print(b1)
print(b2)
print(b3)
print(id(a1))
print(id(b1))
CSV文件操作
CSV(Comma Separated Values)是逗号分隔符文本格式,常用于数据交换、Excle文件和数据库的导入和导出。与Excel文件不同,CSV文件中:
- 值没有类型,所有值都是字符串
- 不能指定字体颜色等样式
- 不能指定单元格的宽高,不能合并单元格
- 没有多个工作表
- 不能嵌入图像图表
python标准库中的模块csv提供了读取和写入csv格式文件的对象。
我们在Excel中建立一个简单的表格:
另存为“csv(逗号分隔)”,打开文件后:
姓名,年龄,星球,次数
张三,20,金星,50000
李四,21,火星,20000
王五,22,水星,60000
import csv
with open(r"csv.csv","r") as f:
a_csv = csv.reader(f)
# print(list(a_csv))
for row in a_csv:
print(row)
import csv
with open(r"e.csv", "w") as f:
b_csv = csv.writer(f)
b_csv.writerow(['张三', 20, '金星', 50000])
b_csv.writerow(['李四', 21, '火星', 20000])
c = [['王五', 22, '水星', 60000], ['赵六', 23, '木星', 80000]]
b_csv.writerows(c)
os和os.path模块
os模块可以帮助我们直接对操作系统进行操作。我们可以直接调用操作系统可执行文件、命令,直接操作文件、目录等等。是系统运维的核心基础。
os调用操作系统文件和命令
- os.system可以帮助我们直接调用系统的命令 ```python import os
os.system(“notepad.exe”) os.system(“regedit”) os.system(“ping www.baidu.com”) os.system(“cmd”)
- os.startfile直接调用可执行的文件<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/1315343/1649343024469-fac27e6b-2ea3-44a4-8e04-e4cfb206b048.png#clientId=ue7981ec4-c1b3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=199&id=u8fb52d32&margin=%5Bobject%20Object%5D&name=image.png&originHeight=249&originWidth=956&originalType=binary&ratio=1&rotation=0&showTitle=false&size=37275&status=done&style=shadow&taskId=u5e15734f-f763-4815-8ea2-3379eab41a9&title=&width=764.8)
<a name="OQjW4"></a>
### os模块-文件和目录操作
我们可以通过文件对象实现对文件内容的读写和操作。如果,还需要对文件和目录做其他操作,可以使用os和os.path模块
**os模块下常用操作文件的方法**
| **方法名** | **描述** |
| --- | --- |
| remove(path) | 删除指定的文件 |
| rename(src,dest) | 重命名文件和目录 |
| stat(path) | 返回文件的所有属性 |
| listdir(path) | 返回path目录下的文件和目录列表 |
**os模块下关于目录操作的相关方法,如下:**
| **方法名** | **描述** |
| --- | --- |
| mkdir(path) | 创建目录 |
| makedirs(path1/path2/path3/...) | 创建多级目录 |
| rmdir(path) | 删除目录 |
| removedirs(path1/path2...) | 删除多级目录 |
| getcwd() | 返回当前工作目录:current work dir |
| chdir(path) | 把path设为当前工作目录 |
| walk() | 遍历目录树 |
| sep | 当前操作系统所使用的路径分隔符 |
```python
import os
#获取当前系统类型
print(os.name) #windows->nt linux和unix ->posix
#当前操作系统所使用的路径分隔符
print(os.sep) #windows->\ linux和unix ->/
#repr() 函数将对象转化为供解释器读取的形式
#os.linesep给出当前平台使用的行终止符
print(repr(os.linesep))
#获取文件的信息
print(os.stat("m1.py"))
#coding=utf-8
#测试os模块中,关于文件和目录操作
import os
#改变当前工作目录为d盘
os.chdir("d:")
#当前目录下创建一个工作目录
os.mkdir("又一个文件")
#删除当前工作目录
os.rmdir("又一个文件") #相对路径相当于当前工作目录
#创建多级目录
os.makedirs("书/文档/python")
#删除多级目录
os.removedirs("书/文档/python") #一只能删除空目录
#../指的是上一级目录
os.makedirs("../书/文档/python")
#重命名目录名称
os.rename("书","电子书")
#获取目录下的文件
dirs = os.listdir("书")
print(dirs)
os.path模块
os.path模块提供了目录相关(路径判断、路径切分、路径链接、文件夹遍历)的操作
方法 | 描述 |
---|---|
isabs(path) | 判断path是否绝对路径 |
isdir(path) | 判断path是否为目录 |
isfile(path) | 判断path是否为文件 |
exists(path) | 判断指定路径的文件是否存在 |
getsize(filename) | 返回文件的大小 |
abspath(path) | 返回绝对路径 |
dirname(p) | 返回目录的路径 |
getatime(filename) | 返回文件的最后访问时间 |
getmtime(filename) | 返回文件的最后修改时间 |
walk(top,func,arg) | 递归方式遍历目录 |
join(path,*paths) | 连接多个path |
split(path) | 对路径进行分割,以列表形式返回 |
splitext(path) | 从路径中分割文件的扩展名 |
#coding=utf-8
import os
import os.path
#判断:绝对路径、是否目录、是否文件、文件是否存在
print(os.path.isabs("d:/a.txt"))
print(os.path.isdir("d:/a.txt"))
print(os.path.isfile("d:/a.txt"))
print(os.path.exists("d:/a.txt"))
#获取文件基本信息
print(os.path.getsize("d:/a.txt"))
print(os.path.dirname("d:/a.txt"))
#文件创建访问修改时间
print(os.path.getctime("d:/a.txt"))
print(os.path.getatime("d:/a.txt"))
print(os.path.getmtime("d:/a.txt"))
#对路径的操作
path = os.path.abspath("b.txt")
print(os.path.split(path))
print(os.path.splitext(path))
print(os.path.join("aa","bb","cc"))
#coding=utf-8
#列出工作目录下所有的py文件,并输出文件名
import os
path = os.getcwd()
file_list = os.listdir(path) #列出子目录、子文件
for filename in file_list:
if filename.endswith("py"): #字符串是否以指定后缀结尾
print(filename)
print("#########################")
#推导式
file_list2 = [filename for filename in os.listdir(path) if filename.endswith(("py"))]
for f in file_list2:
print(f,end="\t")
walk()递归遍历所有文件和目录
os.walk()方法:
返回一个3个元素的数字,(dirpath,dirnames,filenames)
- dirpath:列出指定目录的路径
- dirnames:目录下的所有文件夹
- filenames:目录下所有文件
#coding=utf-8
import os
#os.getcwd() 方法用于返回当前工作目录。
path = os.getcwd()
list_file = os.walk(path)
for dirpath, dirnames, filenames in list_file:
for dir in dirnames: #文件夹
print(dir)
print(os.path.join(dirpath,dir))#全路径
for file in filenames: #文件
print(file)
print(os.path.join(dirpath,file))#全路径
shutil模块(拷贝和压缩)
shutil模块是python标准库中提供的,主要用来做文件和文件夹的拷贝、删除、移动等;还可以做文件和文件夹的压缩,解压操作。
os模块提供了对目录或文件的一般操作。shutil模块作为补充,提供了复制、移动、压缩、解压等操作,这些os模块都没有提供。
拷贝
#coding=utf-8
#shutil模块拷贝、压缩
import shutil
shutil.copyfile("1.txt","1_copy.txt")
shutil.copytree("book/a","book_copy")#当“book_copy”目录不存在时才能正常拷贝
shutil.copytree("book/b","book_copy2", ignore = shutil.ignore_patterns("*.html"))
压缩
shutil模块压缩
# coding=utf-8
import shutil
# 存放的位置 #压缩的格式 #压缩文件
shutil.make_archive("book/new", "zip", "book/a")
zipfile模块解压缩
#coding=utf-8
import zipfile
#压缩
z1 = zipfile.ZipFile("z.zip","w")
z1.write("book")
z1.write("book_copy2/bb.txt")
z1.close()
#解压缩
z2 = zipfile.ZipFile("z.zip","r")
z2.extractall("book_copy")
z2.close()
递归算法
递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接地调用自己
比如斐波那契数列、汉诺塔、快排等问题
递归结构包括两个部分:
- 定义递归头部:如果没有头部,将会陷入死循环,也就是递归条件结束
- 递归体:什么时候需要调用自身方法
#coding=utf-8
num = 1
def a1():
global num #如果要在函数内改变全局变量的值,要增加global关键字声明
num +=1
print("a1")
if num<3:
a1()
a1()
#coding = utf-8
def factorial(n):
if n==1:
return n
else:
return n*factorial(n-1)
print(factorial(5))
#coding=utf-8
import os
def getAllFiles(path):
childFiles =os.listdir(path)
for file in childFiles:
filepath = os.path.join(path,file)
if os.path.isdir(filepath):
getAllFiles(filepath)
print(filepath)
getAllFiles("test_os")