15. 文件操作
一、什么是文件
文件(file)通常是磁盘或固态硬盘上的一段已命名的存储区。它是指一组相关数据的有序集合。这个数据集合有一个名称,叫做文件名。文件名 是文件的唯一标识,以便用户识别和引用。文件名包括 3 个部分:文件路径 + 文件名主干 + 文件后缀名。所有的文件都通过流进行输入、输出操作。
二、文件的基本操作
在 Python 中,内置了文件(File)对象。在使用文本对象时,首先需要通过内置的 opern() 方法创建一个文件对象,然后通过该对象提供的方法向文件中写入内容,以其使用 close() 方法关闭文件。
2.1、创建和打开文件
在 Python 中,想要操作文件需要先创建或者打开指定的文件并创建文件对象,可以通过内置的 open() 函数来实现。open() 函数的定义如下:
open(file, mode='r', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
参数说明:
file
:要创建或打开的文件名称,需要使用单引号或双引号括起来。如果要打开的文件和当前文件在同一个目录下,那么直接写文件名即可,否则需要指定完整的路径。mode
:可选参数,用于指定文件的打开模式。默认的打开模式为只读(r)模式。buffering
:可选参数,用于指定读写文件的缓冲模式。值为 0 表示不缓存,值为 1 表示缓存。如果大于 1,则表示缓冲区的大小。默认为缓存模式。
mode 参数的参数值:
模式 | 含义 | 说明 |
---|---|---|
r | 以只读模式打开文件,文件的指针将会放在文件的开头 | 文件必须存在 |
r+ | 打开文件后,可以读取文件内容,也可以写入新的内容覆盖原有内容(从文件头开始覆盖) | |
rb | 以二进制的格式打开文件,并且采用只读模式。文件的指针将会放在文件的开头,一般用于非文本文件 | |
rb+ | 以二进制的格式打开文件,并且采用读写模式。文件的指针将会放在文件的开头,一般用于非文本文件 | |
w | 以只写模式打开文件 | 文件存在,则将其覆盖,否则创建新的文件 |
w+ | 打开文件后,先清空所有内容,使其变为一个空的文件,对这个空文件有读写权限 | |
wb | 以二进制的格式打开文件,并且采用只写模式。文件的指针将会放在文件的开头,一般用于非文本文件 | |
wb+ | 以二进制的格式打开文件,并且采用读写模式。文件的指针将会放在文件的开头,一般用于非文本文件 | |
a | 以追加模式打开一个文件 | 如果文件已存在,文件指针放在文件的末尾(即新内容会被写入到已有内容之后),否则,创建新文件 |
a+ | 以读写模式打开文件 | |
ab | 以二进制的格式打开文件,并且采用追加模式。文件的指针将会放在文件的开头,一般用于非文本文件 | |
ab+ | 以二进制的格式打开文件,并且采用读写模式。文件的指针将会放在文件的开头,一般用于非文本文件 | |
x | 独占模式创建文件 | 如果文件不存在则创建,存在则报错,并且只能写不能读 |
# open()函数返回一个对象,这个对象就代表了当前打开的文件
file_object = open("text.txt")
print(file)
在 windows 系统中使用路径时,可以用 /
代替 \,可以使用 \\
进行转义,还可以使用原始字符串开代替。表示路径时,我们可以使用 ..
返回上一级目录,使用 '.' 表示当前目录。如果目标文件距离当前文件比较远,此时可以使用绝对路径。绝对路径应该从磁盘的根目录开始书写。
# 使用/代替\
file_name = "./file/text.txt"
# 使用\\进行转义
# file_name = ".\\file\\text.txt"
# 使用原始字符串
# file_name = r".\file\text.txt"
# open()函数返回一个对象,这个对象就代表了当前打开的文件
file_object = open(file_name)
print(file_object)
在默认情况下,使用 open() 函数打开一个不存在的文件会报以下错误:
FileNotFoundError: [Errno 2] No such file or directory: 'hello.txt'
使用 open() 函数以写模式打开一个不存在的文件,Python 会先创建文件,然后再往里面写内容。如果要写入的文件所在目录不存在,就会报错。
FileNotFoundError: [Errno 2] No such file or directory: './files/data.txt'
2.2、文件的关闭
关闭文件后可以使用文件对象的 close() 方法。close() 方法先刷新缓冲区中还没有写入的信息,然后再关闭文件,这样可以将没有写入到文件的内容写入到文件中。在关闭文件后,便不能再进行写入操作。close() 方法的语法格式如下:
file_object.close()
在 Python 中可以使用 with 语句,从而实现在处理文件时,无论是否抛出异常,都能保证 with 语句执行完毕后关闭已经打开的文件。with 语句的基本语法如下:
with open(file_object) as 文件对象:
# 在with语句中可以直接用文件对下个做文件操作
# 此时这个文件只能在with中使用,一旦with结束这个文件对象会自动close
三、文件的读取
3.1、写入文件内容
在 Python 中,提供 open() 方法来向文件中写入内容。如果操作的是一个文本对象的话,则 write() 需要传递一个字符串作为参数。该方法可以分多次向文件中写入内容,写入完成以后,该方法会返回写入字符的个数。
file_object.write("文件内容") # 将指定的内容写到指定文件中
写入文件的前提是在打开文件时,指定模式为写模式或读写模式,否则将会报以下错误:
io.UnsupportedOperation: not writable
如果写入的文件乱码,可以在打开文件时指定编码格式。
在写入文件后,一定要调用 close() 方法关闭文件,否则写入的内容不会保存到文件中。这是因为当我们在写入文件内容时,操作系统不会立刻数据写入磁盘,而是先缓存起来,调用 close() 方法时,操作系统会保证把没有写入的数据全部写入到磁盘。
在向文件写入内容后,如果我们不想马上关闭,也可以调用文件对象提供的 flush() 方法,把缓冲区的数据写入文件,这样也能保证数据全部写入磁盘。
file_name = "text.txt"
lines = [
"滕王高阁临江渚,佩玉鸣鸾罢歌舞。\n",
"画栋朝飞南浦云,珠帘暮卷西山雨。\n",
"闲云潭影日悠悠,物换星移几度秋。\n",
"阁中帝子今何在?槛外长江空自流。\n"
]
try:
with open(file_name,'w',encoding='utf-8') as file_object:
# 向文件中写入内容:
for line in lines:
file_object.write(line)
except FileNotFoundError:
print(f"{file_name}不存在!")
我们可以使用 writelines() 方法将列表中的内容一次写入到指定文件中。
file_object.writelines(list) # 将列表中的内容一次写入到指定文件中
file_name = "text.txt"
lines = [
"滕王高阁临江渚,佩玉鸣鸾罢歌舞。\n",
"画栋朝飞南浦云,珠帘暮卷西山雨。\n",
"闲云潭影日悠悠,物换星移几度秋。\n",
"阁中帝子今何在?槛外长江空自流。\n"
]
try:
with open(file_name,'w',encoding='utf-8') as file_object:
# 向文件中写入内容:
file_object.writelines(lines)
except FileNotFoundError:
print(f"{file_name}不存在!")
如果我们在 open() 函数以二进制的模式读写文件时,不能指定编码 encoding 参数,否则会报错。并且,我们要写入的内容必须是字节类型的数据。
file_name = "text.txt"
lines = [
"滕王高阁临江渚,佩玉鸣鸾罢歌舞。\n",
"画栋朝飞南浦云,珠帘暮卷西山雨。\n",
"闲云潭影日悠悠,物换星移几度秋。\n",
"阁中帝子今何在?槛外长江空自流。\n"
]
try:
with open(file_name,'wb') as file_object:
# 向文件中写入内容:
for line in lines:
file_object.write(line.encode("utf-8"))
except FileNotFoundError:
print(f"{file_name}不存在!")
write() 和 writelines() 函数不会在字符串的末尾自动添加换行符,需要我们手动添加 \n。
3.2、读取文件内容
3.2.1、读取指定字符
文件对象提供 read() 方法读取指定个数的字符,语法格式如下:
file_object.read([size])
size 为可选参数,在文本模式下用来指定读取字符的个数。如果省略要读取字符的个数,则一次性读取所有内容。默认情况 read() 方法是一字节一字节的读取,效率比较低。在二进制模式下,用于指定读取的字节数。
读取文件的前提是在打开文件时,指定模式为读模式或读写模式,否则将会报以下错误:
io.UnsupportedOperation: not readable
file_name = "text.txt"
try:
with open(file_name,encoding="utf-8") as file_object:
# 向文件中写入内容:
content = file_object.read()
print(content)
except FileNotFoundError:
print(f"{file_name}不存在!")
read() 函数可以接收一个 size 作为参数,该参数用来指定要读取字符的数量。每一次读取都是从上一次读取到的位置开始读取的。如果字符的数量小于 size,则会读取剩余所有的字符。如果已经读取到文件的末尾,则会返回空串。
file_name = "text.txt"
try:
with open(file_name,encoding="utf-8") as file_object:
# 定义一个变量指定每次读取的大小
size = 8
# 创建一个循环读取文件内容
while True:
content = file_object.read(size)
if not content:
# 内容读取完毕,退出循环
break
# 向文件中写入内容:
print(content,end='')
print()
except FileNotFoundError:
print(f"{file_name}不存在!")
如果我们在 open() 函数以二进制的模式读写文件时,不能指定编码 encoding 参数,否则会报错。
file_name = "text.txt"
try:
with open(file_name,mode="rb") as file_object:
# 向文件中写入内容:
content = file_object.read()
print(content.decode("utf-8"))
except FileNotFoundError:
print(f"{file_name}不存在!")
使用 read() 方法读取文件时,是从文件的开头开始的。如果想要读取部分内容,可以先使用文件对象的 seek() 方法将文件的指针移动到新位置,然后再应用 read() 方法读取。
3.2.1、读取一行
Python 中还提供了 readline() 方法用于每次读取一行数据。
file_object.readline() # 读取一行
file_name = "text.txt"
try:
with open(file_name,encoding="utf-8") as file_object:
while True:
content = file_object.readline()
if not content:
break
print(content,end='')
print()
except FileNotFoundError:
print(f"{file_name}不存在!")
3.2.2、读取全部行
Python 中还提供 readlines() 方法用于读取全部行。该方法返回的是一个字符串列表,每个元素为文件的一行内容。
file_object.readlines() # 读取全部行,结果保存在列表中
file_name = "text.txt"
try:
with open(file_name,encoding="utf-8") as file_object:
list = file_object.readlines()
for item in list:
print(item,end='')
print()
except FileNotFoundError:
print(f"{file_name}不存在!")
四、文件的定位
我们可以用 seek() 函数修改文件指针的位置。
file_object.seek(offset[,whence]) # 用来修改当前读取的位置
- offsets:在文本模式下用于指定字节的个数,其具体位置与 whence 有关;
- whence:用于指定从什么位置开始计算。值为 0 表示从头开始计算,值 1 表示当前位置开始计算,值 2 表示从文件尾开始计算。默认值为 0;
如果参照物是文件末尾位置,应该倒着移动;
只有 whence=0 可以在文本模式下使用;
我们还可以使用 tell() 函数查看当前读取的位置。
file_object.tell() # 用来查看当前读取的位置
file_name = "text.txt"
try:
with open(file_name,encoding="utf-8") as file_object:
file.seek(30)
print(file_object.tell())
content = file_object.readline()
print(content)
except FileNotFoundError:
print(f"{file_name}不存在!")
五、目录与文件其它操作
目录也称文件夹,用于分层保存文件。通过目录可以分别识别地存放文件。我们可以通过目录快速找到想要的文件。在 Python 中,并没有提供直接操作目录的函数或者对象,而是需要使用内置的 os 和 os.path 模块实现。
os 模块提供与目录相关的函数:
os.getcwd() # 返回当前工作目录
os.listdir(path) # 返回指定目录下的文件和目录信息
os.mkdir(path[,mode]) # 创建目录
os.makedirs(path1/path2...[,mode]) # 创建多级目录
os.rmdir(path) # 删除目录
os.removedirs(path1/path2...) # 删除多级目录
os.chdir(path) # 把path设置为当前工作目录
os.walk(top[,topdown[,onerror]]) # 遍历目录,返回一个元组,包括所有路径名、所有目录列表和文件列表3个元素
os.path 模块提供的与目录相关的函数:
os.path.abspath(path) # 用于获取文件或目录的绝对路径
os.path.exists(path) # 用于判断目录或者文件是否存在,如果存在则返回True,否则返回False
os.path.join(path,name) # 将目录与目录或者文件名拼接起来
os.path.splitext() # 分离文件名和扩展名
os.path.basename(path) # 从一个目录中提取文件名
os.path.dirname(path) # 从一个路径中提取文件路径,不包含文件名
os.path.isdir(path) # 用于判断是否为有效路径
os.path.getsize(path) # 获取文件或文件夹的大小,单位为字节
os 模块提供的与文件相关的函数:
os.access(path,accessmode) # 获取对文件是否有指定访问权限
os.chmod(path,mode) # 修改path指定文件的访问权限
os.remove(paht) # 删除path指定的文件路径
os.rename(src,dst) # 将文件或目录src重名为dst
os.stat(path) # 返回path指定文件的信息
os.startfile(path[,operation]) # 使用关联的应用程序打开path指定的文件
import os
path = os.path.join("db","demo","xxx.txt") # 路径的拼接
print(f"文件的路径:{path}")
parent_directory = os.path.dirname(path) # 获取上级目录
print(f"文件的父目录:{parent_directory}")
is_exist = os.path.exists(parent_directory)
if not is_exist:
os.makedirs(parent_directory) # 创建文件夹
absolute_path = os.path.abspath(path) # 获取绝对路径
print(f"文件的绝对路径:{absolute_path}")
with open(absolute_path,mode="w",encoding="utf-8") as f:
f.write("落霞与孤鹜齐飞,秋水共长天一色。")
grand_parent = os.path.dirname(parent_directory)
print(f"祖父文件夹:{grand_parent}")
file_list = os.listdir(grand_parent) # 查看目录下的文件或一级文件夹
print(f"祖父文件夹下的文件和一级目录:{file_list}")
file_generate = os.walk(grand_parent) # 查看目录下的所有文件,返回的是一个生成器
for base_dir,folder_list,file_list in file_generate:
print(f"base_dir = {base_dir}, folder_list = {folder_list}, file_list = {file_list}")
for name in file_list:
file_path = os.path.join(base_dir,name)
print(f"祖父文件夹下的文件和目录:{file_path}")
os.remove(absolute_path) # 删除文件