文件操作(IO技术)

一个完整的程序一般都包括数据的存储和读取。实际开发中,一般都需要从外部存储介质(硬盘、光盘、U盘 等)读取数据,或者将程序产生的数据存储到文件中,实现持久化保存。

很多软件是将数据存储在数据库中,数据库实际也是基于文件形式存储的。

文本文件和二进制文件

按文件中数据组织形式,我们把文件分为文本文件和二进制文件两大类。

  1. 文本文件

文本文件存储的是普通“字符”文本,默认为Unicode字符集,可以用记事本打开。但是,像word软件编辑文档不是文本文件。

  1. 二进制文件

二进制文件把数据内容用“字节”进行存储,无法用记事本打开,必须使用专用的软件解码,常见的有: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’就能保留原有的样子

文本文件的写入

基本的文件写入操作

文本文件的写入一般就是三个步骤:

  1. 创建文件对象
  2. 写入数据
  3. 关闭文件对象

image.png

write()/writelines()写入数据

image.png

write(a):把字符串a写入到文件中
writelines(b):把字符串列表写入到文件中,不添加换行符

文本文件的读取

文本文件读取一般使用如下三个方法:

  1. read([size])

从文件中读取size个字符,并作为返回结果、如果没有size参数,则读取整个文件。读取到文件末尾,会返回空字符串。

  1. readline()

读取一行内容作为返回结果。读取到文件末尾就会返回空字符串

  1. readlines()

文本文件中,每一行作为一个字符串存入到列表中,返回该列表
image.png

  1. with open(r"d.txt",encoding="utf-8") as f:
  2. for a in f:
  3. print(a,end='')
  1. with open(r"d.txt","r",encoding="utf-8") as f:
  2. while True:
  3. fragment = f.readline()
  4. if not fragment:
  5. break
  6. else:
  7. print(fragment,end="")

image.png

常见的编码介绍

在操作文本文件时,经常会操作中文,这时候就会碰到乱码问题。下面为各种编码之间的关系

python[进阶一] - 图5

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
image.png

image.png

close()关闭文件流

由于文件底层是由操作系统控制,所以我们打开的文件对象必须显示调用close()方法关闭文件对象。当调用close()方法时,首先会把缓冲区数据写入文件(也可以直接调用flush()方法,再关闭文件,释放文件对象。

为了确保文件对象正常关闭,一般结合异常机制的finally或者with关键字实现无论何种情况都能关闭打开的文件对象。

  1. try:
  2. f = open(r"a.txt","a")
  3. str = "abc"
  4. f.write(str)
  5. except BaseException as e:
  6. print(e)
  7. finally:
  8. f.close()

with语句(上下文管理器)

with关键字(上下文管理文件写入操作)可以自动管理上下文资源,无论什么原因跳出with块,都能确保文件正确的关闭,并且可以在代码块执行完毕后自动还原进入该代码块时的现场。

  1. with open(r"c.txt","a") as f:
  2. f.write("abc")

enumerate()函数

  1. a = ["你\n","你好\n","你好啊\n"]
  2. b = enumerate(a)
  3. print(a)
  4. print(list(b))
  5. c = [temp.rstrip()+"#"+str(index) for index,temp in enumerate(a)]
  6. #rstrip()去除空白符\n
  7. #temp 临时变量你,你好,你好啊
  8. #index #0 #1 #2
  9. print(c)

image.png

  1. with open(r"d.txt","r",encoding="utf-8") as f:
  2. lines = f.readlines()
  3. lines = [line.rstrip()+"#"+str(index+1)+"\n" for index,line in enumerate(lines)]
  4. #推导式
  5. with open(r"d.txt","w",encoding="utf-8") as f:
  6. f.writelines(lines)

image.png

二进制文件的读取和写入

二进制文件的处理流程和文本文件流程一致。首先我们还是要创建文件对象,不过,我们需要指定二进制模式,从而创建出二进制文件对象。例如:

f = open(r"d:\a.txt","wb") #可写的、重写模式二进制对象
f = open(r"d:\a.txt","ab") #可写的、追加模式的二进制对象
f = open(r"d:\a.txt","rb") #可读的二进制文件对象

创建好二进制文件对象后,仍然可以使用write()、read()实现文件的读写操作

  1. with open(r"picture1.png","rb") as f:
  2. with open(r"picture2.png","wb") as w:
  3. for line in f.readlines():
  4. w.write(line)
  5. 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()

  1. with open(r"d.txt","r",encoding="utf-8") as f:
  2. print("文件名为:{0}".format(f.name))
  3. print(f.tell())
  4. print("读取的内容:{0}".format(str(f.readline())))
  5. print(f.tell())
  6. f.seek(3)
  7. print("读取的内容:{0}".format(str(f.readline())))

image.png

使用pickle序列化

python中,一切皆对象,对象本质上就是一个“存储数据的内存块”有时候,我们需要将“内存块的数据”保存到硬盘上,或者通过网络传输到其他计算机上,这时候,就需要“对象的序列化和反序列化”。对象的序列化机制广泛的应用在分布式、并行系统上。

  1. 序列化指的是:将对象转化成“串行化”数据形式,存储到硬盘或者网络传输到其他地方。反序列化是指相反的过程,将读取到的“串行化数据”转化成对象<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))

image.png

CSV文件操作

CSV(Comma Separated Values)是逗号分隔符文本格式,常用于数据交换、Excle文件和数据库的导入和导出。与Excel文件不同,CSV文件中:

  • 值没有类型,所有值都是字符串
  • 不能指定字体颜色等样式
  • 不能指定单元格的宽高,不能合并单元格
  • 没有多个工作表
  • 不能嵌入图像图表

python标准库中的模块csv提供了读取和写入csv格式文件的对象。
我们在Excel中建立一个简单的表格:
image.png

另存为“csv(逗号分隔)”,打开文件后:

image.png

姓名,年龄,星球,次数
张三,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)

image.png

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)

image.png

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"))

image.png

#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)

image.png

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"))

image.png

#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")

image.png

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))#全路径

image.png

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"))

image.png

压缩

shutil模块压缩

# coding=utf-8

import shutil

# 存放的位置    #压缩的格式     #压缩文件
shutil.make_archive("book/new", "zip", "book/a")

image.png

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()

image.png
image.png

递归算法

递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接地调用自己
比如斐波那契数列、汉诺塔、快排等问题

递归结构包括两个部分:

  • 定义递归头部:如果没有头部,将会陷入死循环,也就是递归条件结束
  • 递归体:什么时候需要调用自身方法
#coding=utf-8

num = 1
def a1():
    global num  #如果要在函数内改变全局变量的值,要增加global关键字声明
    num +=1
    print("a1")
    if num<3:
        a1()

a1()

image.png

#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")

image.png