联想说 Python 基础 19 - 文件
文件
目标:掌握文件的基本操作。
不知道大家是否有发现,前面所学的内容,不管写成什么样的程序,当重启后,永远无法继续使用。就像是小时候玩的游戏,它没有存档功能,断电就丢失了。
为什么会产生这样的问题呢?
因为程序是运行在内存中的,而内存这么珍贵,怎么能用它来长期存储这些数据呢?就像你们的学习资料,一部小心就上 G 了,多学几个那还怎么玩。在座的各位学习资料应该不少吧,比如唱歌的、做饭的、敲代码的还有....,怎么也得上百 G,放在内存合适吗?那不得亏死你。
所以我们一般存储文件是存储在磁盘中的(上 T 的那个,至少也几百 G),而程序运行则是在内存中(8G、16G 的那个)。
要让程序能实现 “存档” 功能,那需要让程序能操作磁盘才行,这节就学这个。
文件基本操作
文件这个东西,相信大家都不陌生,所以就不说它了,我们直接开始用程序操作它。
用程序怎么操作呢?
大家回想一下你们自己是怎么操作文件的,比如向一个文本中写入内容:
- 找到文件所在位置,没有就新建一个
- 打开文件
- 把鼠标点击到需要写入内容的地方
- 写入内容
- 保存、关闭
差不多就这么几步了。
其实程序也差不多,它也需要找到指定文件,指定位置才能操作。根据上面步骤,我们用程序来实现一下:
# 打开 xx 目录下的 xx 文件
file = open("D:/xx/test.txt", "w")
# 向里面写入内容
file.write("hello world")
# 关闭
file.close()
说明:
-
open():表示打开,它可以用来打开文件第一个参数:表示文件地址,包含文件后缀
第二个参数:打开的模式,写 w 就行这个后面讲
返回值:返回一个文件对象
-
file.write():表示向这个文件中写入内容 -
file.close():关闭文件
上面程序执行完成后,控制台并不会打印什么,但是在目标文件中写入 “hello world”。
访问模式
在打开文件的时候,需要传递两个参数,第二个参数表示的是打开文件的模式。什么叫打开的模式?
比如我们打开一本书,如果仅仅是读的话,那翻开书看就行了。那如果我们打开的是作业本,是不是还得拿上笔准备写。
如果对计算机有一些了解的话,你们也会发现文件的限制,比如有的文件是只读,你只能看,不能写。
类似于这些,就是我们所说的访问模式了,也就是我们打开文件需要做什么,能够做什么,根据我们的需求用不同的模式打开。
常用的模式有:
r:只读模式,如果文件不存在则报错w:写入模式,如果文件不存在则创建,如果文件存在则清空文件然后写入a:追加模式,如果文件不存在则创建,如果文件存在则在文件末尾追加内容wb:以二进制格式写入。如果该文件已存在,则将其覆盖;如果该文件不存在,创建新文件写入rb:以二进制格式只读。文件指针将会放在文件的开头
r、w、a 这几个,自己试试就好,这里主要说说 b,表示以二进制格式读写。
什么是二进制?什么文件才会以二进制格式读写?
十进制都知道,逢十进一,二进制就是逢二进一,也就是说在二进制中,你永远看不到 2 及以上的数字,也就是 0 和 1 了。
至于那些文件才会以二进制格式读写,除了文本文件外,其他文件基本都使用二进制格式。比如 图片、音乐、视频等等。由于二进制文件的内容我们看不懂,所以这里演示就不用二进制格式的了。
文件读写
在最开始有一个程序,它实现了向文件内写入字符串。不过它并没有完,还有一些其他的我们需要说一说的。
文件读写的一些方法
主要有这么一些方法:
-
file.write(data): 将数据写入文件它接受一个字符串参数,并将其写入文件。
如果文件不存在,它将创建一个新文件。如果文件已存在,它将覆盖文件中的任何现有内容。
-
file.read(n): 从文件中读取最多 n 个字符的数据它返回从文件开始到当前文件指针位置的字节串。如果未指定 n,则
默认读取整个文件。 -
file.readline():读取文件中的一行数据它返回一个字符串,该字符串包含文件中的下一行。
-
file.readlines(): 一次读取整个文件的内容并按行分割它返回一个列表,其中每个元素都是文件中的一行。
-
file.tell(): 返回当前文件指针的位置文件指针是一个内部变量,它表示下一次读取或写入操作开始的位置。
-
file.seek(offset, whence):移动文件指针到指定位置offset 参数表示移动的字节数;
whence 参数决定了基准点在哪里:
- 如果whence为0,表示从文件的开头开始;
- 如果whence为1,表示从当前位置开始;
- 如果whence为2,表示从文件的末尾开始。
-
file.close():关闭打开的文件,释放其占用的系统资源
演示 - 读取文件所有内容:
文件内容是蜀道难文章,可以自行摘抄。
# 打开 xx 目录下的 xx 文件
file = open("D:/xx/test.txt", "r")
contents = file.read()
print(contents) # 打印读取到的内容
# 关闭
file.close()
如果代码是这样写的话,会出现一个错误:UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 10: illegal multibyte sequence。解决方式很简单,只需要在 open 函数中添加第三个参数,并设置值:
file = open("D:/xx/test.txt", "r", encoding="utf-8")
这样就能正常访问到了(至于原因,我们放到后面再拓展)。
除了 read 外,还有几个方法也可以读取,咱们就不一一测试,直接全部看看:
# 读取 n 个字符
n_contents = file.read(10)
print(n_contents) # 蜀
print("-------------------")
# 读取一行
line_contents = file.readline()
print(line_contents) # 道难
print("-------------------")
# 读取整个文件,按行分割
lines_contents = file.readlines()
print(lines_contents) # ['【作者】李白 【朝代】唐\n', ...,侧身西望长咨嗟!']
上面的代码,有几个需要注意的:
-
read(10):空格也是字符,也会算在 n 里面
-
这里的结果是 “道难”,为什么呢?
你可以理解为你为了避免漏掉,所以每读一个就把手指指向下一个,而前面的已经读了 “蜀”,所以这里就是 “道难” 了。
下面一样
-
readlines 这个你们自己试试比较好,最好文章内容有多行,它会把每行都作为列表的一个元素保存起来
指针
这里的指针并非 xx 语言的指针,这里的指针理解为光标就行(光标不知道?看看你文字后面闪烁的那个竖线)。
关于指针的方法代码演示:
# 获取指针位置
point = file.tell()
print(point) # 0
file.read(6)
point2 = file.tell()
print(point2) # 6
# 移动指针
file.seek(9)
point2 = file.tell()
print(point2) # 9
由于文件初次打开,所以指针开始的位置在 0。
当读取了 6 个字符后,指针向后移动,指针的位置是 6。
使用 seek 方法移动指定的位置,指针的位置是指定的位置,不论是已读还是未读。
字符编码
前面在读取文本内容的时候不是出现了一个 UnicodeDecodeError 错误吗?它翻译成中文叫统一编码错误,简单点就 编码错误。
为什么会产生这样的问题呢?为什么后面加上 encoding="utf-8" 后又没问题了呢?
先看看加上的这个是什么。encoding 翻译成中文叫编码,这个好像字面意思不太好理解,那就不费那劲了。
编码是将信息从一种形式转换为另一种形式的过程,使得计算机可以理解和处理这些信息(图片、二进制)。
后面的 utf-8 则是一种编码格式了,也就是说它是把文件的内容编码成了这种格式就没有出错。
下面来看看这种格式是什么
utf-8 全称 Unicode Transformation Format-8 bits,好像也不知道哈。它是一种通用的字符编码标准,可以用来表示包括中文、英文、日文、韩文、阿拉伯文、希腊文等各种语言在内的多种字符集。
在上面的错误提示中还看到一个叫 gbk 的,它是一种汉字编码标准,主要用于汉字的计算机输入和存储。
当我们使用 open 函数不指定编码格式的时候,使用的是系统默认的编码格式。在 Windows 平台上,系统默认的编码格式是 gkb。
又一个问题:他俩都支持中文,为什么还是会报错?
因为文件保存时,会选择一种编码格式,而这里读取的文件的格式是 utf-8,可以在 PyCharm 的右下角看到:

所以在读取文件的时候,需要指定编码格式。
with
with 是 Python 中的一个关键字,主要用于简化资源管理。
在上面操作文件的过程中,都避免不了要关闭文件,但是如果不小心就很容易忘记关闭。虽然说关系不大,但是当打开的太多,又没来得及清理的时候,问题就慢慢来了。
with 就能很好的帮我们处理这个事情,它的使用语法如下:
with 获取资源 [as 对象] :
pass
as 对象,相当于给打开的资源取了个名字。
比如结合 with 打开文件:
with open("test.txt", "r", encoding="utf-8") as file:
print(file.read())
这样,我们就不需要单独的去使用 close 方法来关闭这个文件资源了。
OS
前面主要是用来操作文件,不管是读还是写都可以(写的时候可以创建文件)。但是一般我们都会把文件归类,避免不同的文件存在同一个文件夹下,这样就太乱,不便管理。显然,前面的那些并不能实现这个需求。
我们需要引入 os 模块来实现。os 模块是 Python 自带的标准库模块,它提供了许多方便使用操作系统相关功能的函数。比如:
-
os.getcwd(): 此函数返回当前工作目录的绝对路径 -
os.path.exists(path): 此函数检查指定路径的文件或目录是否存在 -
os.mkdir(path[, mode]): 此函数创建一个新的目录path 是要创建的目录的路径,mode 是可选的,用于设置新目录的权限(默认为0777);如果目录已存在,会报错。
-
os.makedirs(path[, mode]): 此函数递归地创建目录如果目录已经存在,此函数不会抛出错误;mode 参数同样是设置新目录的权限。
-
os.listdir(path): 此函数返回指定路径下的文件和目录列表 -
os.walk(top): 此函数生成在目录树中的文件名(所有)它将生成一个三元组(dirpath, dirnames, filenames),其中:
- dirpath是目录的路径
- dirnames是该目录下子目录的列表
- filenames是该目录下文件的列表
-
os.rmdir(path):删除空目录 -
os.remove(path):删除指定路径的文件 -
os.rename(src, dst):重命名文件或目录的名称
演示:
import os
import shutil
# 1. 获取当前脚本工作的目录路径
current_dir = os.getcwd()
print(current_dir) # 当前代码所在目录
# 2. 判断某个文件夹是否存在
boo = os.path.exists('./resources')
print(boo) # True
# 3. 生成一个新的文件夹,文件夹存在会无法创建 并提示 FileExistsError
if not os.path.exists('./resources'):
os.mkdir('./resources') # 创建文件夹
# 4. 递归创建文件夹,如果存在,出错 可以添加 exist_ok 参数忽略
if not os.path.exists('./resources/haha'):
os.makedirs('./resources/haha') # 递归创建文件夹
# 或者
os.makedirs('./resources/xixi', exist_ok=True)
# 5. 列出指定目录下的所有文件
dirs = os.listdir('./')
print(dirs) # ['01.文件.py', '02.文件.py', 'resources', 'test.txt', 'test1.txt']
# 6. 递归列出
dirs = os.walk('./')
print(list(dirs)) # [('./', ['resources'], ['01.文...... ('./resources\\xixi', [], [])]
# 7. 重命名
if not os.path.exists('./资源'):
os.rename('./resources', './资源')
# 8. 删除空目录 注意:不是空的不能删除,会出现 OSError 错误
if os.path.exists('./资源'):
# os.rmdir('./资源')
shutil.rmtree('./资源') # 强制删除,慎用
# 9. 删除指令路径文件 注意:不能用于删除文件夹
if os.path.exists('./资源'):
open("./资源/test.txt", 'w')
os.remove('./资源')
这些函数基本就是字面意思,在参数和返回值上没有太多复杂的,就不一一说明了(细看注释)。
另外,在操作文件的时候,尽量先判断一下,避免文件不存在或被覆盖。

浙公网安备 33010602011771号