联想说 Python 基础 19 - 文件

文件

目标:掌握文件的基本操作。

不知道大家是否有发现,前面所学的内容,不管写成什么样的程序,当重启后,永远无法继续使用。就像是小时候玩的游戏,它没有存档功能,断电就丢失了。

为什么会产生这样的问题呢?

因为程序是运行在内存中的,而内存这么珍贵,怎么能用它来长期存储这些数据呢?就像你们的学习资料,一部小心就上 G 了,多学几个那还怎么玩。在座的各位学习资料应该不少吧,比如唱歌的、做饭的、敲代码的还有....,怎么也得上百 G,放在内存合适吗?那不得亏死你。

所以我们一般存储文件是存储在磁盘中的(上 T 的那个,至少也几百 G),而程序运行则是在内存中(8G、16G 的那个)。

要让程序能实现 “存档” 功能,那需要让程序能操作磁盘才行,这节就学这个。

文件基本操作

文件这个东西,相信大家都不陌生,所以就不说它了,我们直接开始用程序操作它。

用程序怎么操作呢?

大家回想一下你们自己是怎么操作文件的,比如向一个文本中写入内容:

  1. 找到文件所在位置,没有就新建一个
  2. 打开文件
  3. 把鼠标点击到需要写入内容的地方
  4. 写入内容
  5. 保存、关闭

差不多就这么几步了。

其实程序也差不多,它也需要找到指定文件,指定位置才能操作。根据上面步骤,我们用程序来实现一下:

# 打开 xx 目录下的 xx 文件
file = open("D:/xx/test.txt", "w")
# 向里面写入内容
file.write("hello world")
# 关闭
file.close()

说明:

  • open():表示打开,它可以用来打开文件

    第一个参数:表示文件地址,包含文件后缀

    第二个参数:打开的模式,写 w 就行这个后面讲

    返回值:返回一个文件对象

  • file.write():表示向这个文件中写入内容

  • file.close()关闭文件

上面程序执行完成后,控制台并不会打印什么,但是在目标文件中写入 “hello world”。

访问模式

在打开文件的时候,需要传递两个参数,第二个参数表示的是打开文件的模式。什么叫打开的模式?

比如我们打开一本书,如果仅仅是读的话,那翻开书看就行了。那如果我们打开的是作业本,是不是还得拿上笔准备写。

如果对计算机有一些了解的话,你们也会发现文件的限制,比如有的文件是只读,你只能看,不能写。

类似于这些,就是我们所说的访问模式了,也就是我们打开文件需要做什么,能够做什么,根据我们的需求用不同的模式打开。

常用的模式有:

  1. r只读模式,如果文件不存在则报错
  2. w写入模式,如果文件不存在则创建,如果文件存在则清空文件然后写入
  3. a追加模式,如果文件不存在则创建,如果文件存在则在文件末尾追加内容
  4. wb:以二进制格式写入。如果该文件已存在,则将其覆盖;如果该文件不存在,创建新文件写入
  5. rb:以二进制格式只读。文件指针将会放在文件的开头

r、w、a 这几个,自己试试就好,这里主要说说 b,表示以二进制格式读写。

什么是二进制?什么文件才会以二进制格式读写?

十进制都知道,逢十进一,二进制就是逢二进一,也就是说在二进制中,你永远看不到 2 及以上的数字,也就是 0 和 1 了。

至于那些文件才会以二进制格式读写,除了文本文件外,其他文件基本都使用二进制格式。比如 图片、音乐、视频等等。由于二进制文件的内容我们看不懂,所以这里演示就不用二进制格式的了。

文件读写

在最开始有一个程序,它实现了向文件内写入字符串。不过它并没有完,还有一些其他的我们需要说一说的。

文件读写的一些方法

主要有这么一些方法:

  1. file.write(data): 将数据写入文件

    它接受一个字符串参数,并将其写入文件。

    如果文件不存在,它将创建一个新文件。如果文件已存在,它将覆盖文件中的任何现有内容。

  2. file.read(n): 从文件中读取最多 n 个字符的数据

    它返回从文件开始到当前文件指针位置的字节串。如果未指定 n,则默认读取整个文件

  3. file.readline(): 读取文件中的一行数据

    它返回一个字符串,该字符串包含文件中的下一行。

  4. file.readlines(): 一次读取整个文件的内容并按行分割

    它返回一个列表,其中每个元素都是文件中的一行。

  5. file.tell(): 返回当前文件指针的位置

    文件指针是一个内部变量,它表示下一次读取或写入操作开始的位置。

  6. file.seek(offset, whence): 移动文件指针到指定位置

    offset 参数表示移动的字节数;

    whence 参数决定了基准点在哪里:

    • 如果whence为0,表示从文件的开头开始;
    • 如果whence为1,表示从当前位置开始;
    • 如果whence为2,表示从文件的末尾开始。
  7. 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', ...,侧身西望长咨嗟!']

上面的代码,有几个需要注意的:

  1. read(10):空格也是字符,也会算在 n 里面

  2. 这里的结果是 “道难”,为什么呢?

    你可以理解为你为了避免漏掉,所以每读一个就把手指指向下一个,而前面的已经读了 “蜀”,所以这里就是 “道难” 了。

    下面一样

  3. 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 自带的标准库模块,它提供了许多方便使用操作系统相关功能的函数。比如:

  1. os.getcwd(): 此函数返回当前工作目录的绝对路径

  2. os.path.exists(path): 此函数检查指定路径的文件或目录是否存在

  3. os.mkdir(path[, mode]): 此函数创建一个新的目录

    path 是要创建的目录的路径,mode 是可选的,用于设置新目录的权限(默认为0777);如果目录已存在,会报错。

  4. os.makedirs(path[, mode]): 此函数递归地创建目录

    如果目录已经存在,此函数不会抛出错误;mode 参数同样是设置新目录的权限。

  5. os.listdir(path): 此函数返回指定路径下的文件和目录列表

  6. os.walk(top): 此函数生成在目录树中的文件名(所有)

    它将生成一个三元组(dirpath, dirnames, filenames),其中:

    • dirpath是目录的路径
    • dirnames是该目录下子目录的列表
    • filenames是该目录下文件的列表
  7. os.rmdir(path):删除空目录

  8. os.remove(path):删除指定路径的文件

  9. 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('./资源')

这些函数基本就是字面意思,在参数和返回值上没有太多复杂的,就不一一说明了(细看注释)。

另外,在操作文件的时候,尽量先判断一下,避免文件不存在或被覆盖。

posted @ 2023-10-14 20:55  笔锋微凉~~  阅读(21)  评论(0)    收藏  举报