10. 文件 I/O
I/O (输入/输出)是程序设计中很重要的一个概念,输入允许程序读取外部数据(包括来自磁盘、光盘等存储设备的数据),用户输入数据。输出允许程序将数据输出到磁盘、光盘等存储设备中。
Python 提供了非常丰富的 I/O 支持, os
模块中有大量对文件和目录操作的函数,同时在 Python 中也提供了全局的 open()
函数用于打开文件,在文件打开后,程序既可读取文件的内容,也可向文件输出内容。 Python 提供了多种方式来读写文件内容。
10.1. 目录
在 os
模块下提供了大量操作文件和目录的函数,常用的与目录相关的函数如下所示:
os.getcwd()
:用于获取当前目录os.chdir(path)
:用于改变当前目录os.fchdir(fd)
:通过文件描述符改变当前目录,类似于chdir()
函数os.chroot(path)
:用于改变当前进程的根目录os.listdir(path)
:用于返回参数 path 对应目录下的所有文件和子目录os.mkdir(path[, mode])
:用于创建参数 path 对应的目录,其中 mode 用于指定该目录的权限os.makedirs(path[, mode])
:作用类似于mkdir()
函数,但函数的功能更加强大,它可以递归创建目录os.rmdir(path)
:删除 path 对应的空目录。如果目录非空,则抛出一个OSError
异常os.removedirs(path)
:递归删除目录os.rename(src, dst)
:重命名文件或目录,将src
重名为dst
os.renames(old, new)
:对文件或目录进行递归重命名
除此之外,在 os.path
模块下还提供了一些其他的涉及到目录的函数,如:
os.path.exists(path)
:用于判断指定目录是否存在
这些函数的具体使用方法如下代码所示:
import os
# 当前目录:
base_path = "/Users/zhaozhiyong/Desktop/my_code/python_course"
cur_path = base_path + "/chapter10"
# 获取当前目录
print(os.getcwd()) # /Users/zhaozhiyong/Desktop/my_code/python_course/chapter10
# 改变当前目录
os.chdir(base_path + "/chapter09")
print(os.getcwd()) # /Users/zhaozhiyong/Desktop/my_code/python_course/chapter09
# 返回指定目录下所有文件和子目录
# ['poem.txt', 'menu.py', 'a.txt']
print(os.listdir(base_path))
# 创建目录
print(os.path.exists(cur_path + "/new_folder")) # False
os.mkdir(cur_path + "/new_folder")
# 删除空目录
try:
os.rmdir(cur_path + "/new_folder")
except OSError as e:
print(e)
# 递归创建
os.makedirs(cur_path + "/new_folder1/new_folder2/new_folder3/")
# 递归删除
os.removedirs(cur_path + "/new_folder1/new_folder2/new_folder3/")
# 创建新目录,并修改名字
os.mkdir(cur_path + "/new_folder_name_src")
os.rename(cur_path + "/new_folder_name_src", cur_path + "/new_folder_name_dst")
10.2. 打开文件
要对文件进行 I/O 操作,首先需要打开文件, Python 中提供了 open()
函数用于打开指定文件。
10.2.1. open()
函数
Python 提供的 open()
函数用于打开指定文件。 open()
函数的语法格式如下所示:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
在 open()
函数的语法格式中,只有第一个参数是必需的,该参数代表要打开文件的路径。其余的参数都带有默认值。 open()
函数返回的是一个文件对象。如果该文件不能被打开,则引发 OSError
错误。
在打开文件之后,就可以调用文件对象的属性和方法。文件对象支持如下常见的属性:
file.encoding
:返回文件的编码方式file.closed
:返回文件是否已经关闭file.mode
:返回被打开文件的访问模式file.name
:返回被打开文件的名称
具体的使用方法如下代码所示:
dir = "/Users/zhaozhiyong/Desktop/my_code/python_course/chapter10/"
file_name = dir + "a.txt"
f = open(file_name)
# 文件的编码方式
print(f.encoding) # UTF-8
# 文件的访问模式
print(f.mode) # r
# 文件是否己经关闭
print(f.closed) # False
# 文件对象打开的文件名
# /Users/zhaozhiyong/Desktop/my_code/python_course/chapter10/a.txt
print(f.name)
10.2.2. 文件打开模式 mode
open()
函数支持的文件打开模式如下图所示:
如果程序使用 r 或 r+ 模式打开文件,则要求被打开的文件本身是存在的。也就是说,使用 r 或 r+ 模式都不能创建文件。但如果使用 w 、 w+ 、 a 、 a+ 模式打开文件,则该文件可以是不存在的,
open()
函数会自动创建新文件。
10.2.3. 缓冲 buffering
计算机外设(比如硬盘、网络)的速度远远低于访问内存的速度,而程序执行 l/O 时要么将内存中的数据写入外设,要么将外设中的数据读取到内存,如果不使用缓冲,就必须等外设输入或输出一个字节后,内存中的程序才能输出或输入一个字节,这意味着内存中的程序大部分时间都处于等待状态。
在 open()
函数中, buffering
是一个可选的整数,用于设置缓冲策略。如果 buffering
参数值为 0 时,那么该函数打开的文件就是不带缓冲的,如果该参数的值是 1,则该函数打开的文件就是带缓冲的,此时程序执行 I/O 将具有更好的性能。如果该参数的值是大于 1 的整数,则该整数用于指定缓冲区的大小,其单位是字节。如果该参数的值为任何负数,则代表使用默认的缓冲区大小。
10.3. 读取文件
Python 既可以使用文件对象的方法来读取文件,也可以使用其他模块的函数来读取文件。
10.3.1. 按字节或字符读取
文件对象提供了 read()
方法来按字节或字符读取文件内容,根据 open()
函数中 mode
参数是否设置为 b
,如果设置为 b
则为按字节读取,否则按字符读取。在调用 read()
函数时还可以传入一个整数作为参数,用于指定最多读取多少个字节或宇符。具体使用方法如下代码所示:
dir = "/Users/zhaozhiyong/Desktop/my_code/python_course/chapter01/"
file_name = dir + "a.txt"
f = open(file_name) # 打开文件读操作
while True:
content = f.read(1) # 读取一个字符
if not content:
break
print(content, end='')
f.close() # 关闭文件
在读写完文件之后,需要显式调用 close()
函数关闭已打开的文件。如果在调用 read()
函数时不传入参数,该方法默认会读取全部文件内容。使用方法如下代码所示:
dir = "/Users/zhaozhiyong/Desktop/my_code/python_course/chapter10/"
file_name = dir + "a.txt"
f = open(file_name)
print(f.read()) # 读取全部内容
f.close()
10.3.2. 按行读取
针对文本文件,文件对象提供了以下两个方法来读取行:
readline([n])
:读取一行内容。如果指定了参数n
,则只读取此行内的 n 个字符readlines()
:读取文件内所有行
readlines()
函数的具体使用方法如下代码所示:
dir = "/Users/zhaozhiyong/Desktop/my_code/python_course/chapter10/"
file_name = dir + "a.txt"
f = open(file_name)
while True:
# 一次读一行
line = f.readline()
# 如果读不到数据,即退出
if not line:
break
print(line.strip())
f.close()
上述代码使用 readline()
函数每次读取一行,当读取为空时,即退出读取。同样,也可以使用 readlines()
函数一次性将文件中的所有行都读出来,具体使用方法如下代码所示:
dir = "/Users/zhaozhiyong/Desktop/my_code/python_course/chapter10/"
file_name = dir + "a.txt"
f = open(file_name)
# 一次性读取所有行
for line in f.readlines():
print(line.strip())
f.close()
10.3.3 文件迭代器
实际上,文件对象本身就是可遍历的,可以使用 for
循环来遍历文件内容。具体使用方法如下代码所示:
dir = "/Users/zhaozhiyong/Desktop/my_code/python_course/chapter10/"
file_name = dir + "a.txt"
f = open(file_name)
# 使用 for 循环遍历文件对象
for line in f:
print(line.strip())
f.close()
10.3.4. with
语句
在显式调用 open()
函数打开文件时,都需要显式调用 close()
函数关闭已经打开的文件, open()
函数和 close()
函数都是成对出现的。实际上,在 Python 中提供了 with
语句来管理资源关闭。比如可以把打开的文件放在 with
语句中,这样 with
语句就会帮我们自动关闭文件。 with
语句的语法格式如下所示:
with context_expression [as target(s)]:
with 代码块
将 with
语句与文件打开关闭结合的具体使用方法如下代码所示:
dir = "/Users/zhaozhiyong/Desktop/my_code/python_course/chapter10/"
file_name = dir + "a.txt"
# 使用 with 语句管理资源
with open(file_name) as f:
for line in f:
print(line.strip())
10.4. 写文件
如果 mode
参数的取值为 r+
、 w
、 w+
、 a
、 a+
,则采用写入的模式打开文件。几种模式不同的是,当以 r+
、 w
、 w+
模式打开文件时,文件指针位于文件开头处,而当以 a
、 a+
模式打开文件时,文件指针位于文件结尾处。此外,当以 w
或 w+
模式打开文件时,文件会先被清空。
10.4.1. 文件指针
文件指针用于标明文件读写的位置,文件对象提供了以下方法来操作文件指针:
seek(offset[, whence])
:用于把文件指针移动到指定位置。当whence
为 0 时,表明从文件开头开始计算,如将offset
设为 3 ,就是将文件指针移动到第 3 处; 当whence
为 1 时,表明从指针当前位置开始计算,比如文件指针当前在第 5 处,将offset
设为 3 ,就是将文件指针移动到第 8 处;当whence
为 2 时,表明从文件结尾开始计算,比如将offset
设为 3 ,表明将文件指针移动到文件结尾倒数第 3 处。tell()
:用于判断文件指针的位置。
具体使用方法如下代码所示:
dir = "/Users/zhaozhiyong/Desktop/my_code/python_course/chapter10/"
file_name = dir + "a.txt"
f = open(file_name, "rb")
# 返回当前的文件指针的位置
print(f.tell()) # 0
# 读 10 个字符
f.read(10)
print(f.tell()) # 10
# 移动文件指针
f.seek(20) # 从头移动 20
print(f.tell()) # 20
f.seek(10, 1) # 从当前位置再移动 10
print(f.tell()) # 30
f.seek(10, 2) # 从末尾倒数 10
print(f.tell()) # 150
f.close()
在
seek()
函数中设置whence
参数为 1 或者 2 时,open()
函数的mode
必须是带有b
或者b+
,否则只支持从头移动,此时会报错io.UnsupportedOperation: can't do nonzero cur-relative seeks
。
10.4.2. 输出内容
文件对象提供的写文件的方法主要有如下两个方法:
write()
:输出字符串(str)或字节串(bytes)。只有以二进制模式( b 模式)打开的文件才能写入字节串writelines()
:输出多个字符串或多个字节串
两个函数的具体使用方法如下代码所示:
import os
dir = "/Users/zhaozhiyong/Desktop/my_code/python_course/chapter10/"
file_name = dir + "poem.txt"
f = open(file_name, "w+")
f.write("静夜思——李白" + os.linesep)
f.writelines(("床前明月光,疑似地上霜。" + os.linesep,
"举头望明月,低头思故乡。" + os.linesep))
f.close()
在上述代码中, os.linesep
代表当前操作系统上的换行符。 write()
函数只输出了一个字符串,而 writelines()
函数输出了两个字符串。
10.5. 本章小结
本章主要介绍了与 Python I/O 相关的知识,包括如何管理目录和文件。 Python 的 os
模块下提供了大量与 I/O 相关的函数与方法来操作文件和目录, Python 文件读写都需要通过全局 open()
函数,在使用 open()
函数打开文件时可指定不同的模式,通过不同的模式能以二进制文件和文本文件的方式来打开文件,既可打开文件进行读写,也可打开文件进行追加。在打开文件后,程序能以多种方式读取文件内容,即可按字节/字符读取、按行读取、使用文件迭代器读取。在写入文件内容时比较简单,要么使用 write()
方法输出单独的字符串或字节串,要么使用 writelines()
方法批量输出多个字节串和字符串。
本章需要掌握知识点:
- 掌握
os
模块下与目录和文件相关的常用函数 - 掌握打开文件的
open()
方法 - 掌握几种不同的读取文件的方法
- 掌握写文件的两种方法