Python 中的文件 IO 操作

1. 打开和关闭文件

1.1 打开文件

访问文件前,需要使用用 Python 内置的 open() 函数打开一个文件:

open(path, access_mode)
  • path 是要访问的文件的路径名
  • access_mode 是文件的访问模式
    • 可以是只读、读写、追加等模式,所有可能的取值见 1.2 小节
    • 这个参数是可选的,缺省情况下,是以只读模式 r 打开文件
  • open 返回一个 file 对象,通过调用 file 对象的成员方法访问该文件

使用 open 打开一个文件并访问:

file = open('test.txt')
line = file.readline()
print(line)
  • 在第 1 行,以只读方式打开文件 test.txt,open 返回一个 file 对象
  • 在第 2 行,调用 file 对象的 readline() 方法从文件中读取一行

1.2 文件的访问模式

下表列出了常用的文件访问模式:

模式 描述 如果文件存在 如果文件不存在
r 以只读方式打开文件 保留原有内容,从文件头部开始读 抛出异常 FileNotFoundError
r+ 以读写方式打开文件 保留原有内容,从文件头部开始读 抛出异常 FileNotFoundError
w 以只写方式打开文件 删除原有内容,从文件头部开始写入 创建新文件
w+ 以读写方式打开文件 删除原有内容,从文件头部开始读写 创建新文件
a 以只写方式打开文件 保留原有内容,从文件尾部开始读写 创建新文件
a+ 以读写方式打开文件 保留原有内容,从文件尾部开始读写 创建新文件

1.3 关闭文件

访问文件的步骤如下:

  1. 打开文件
  2. 读写文件
  3. 关闭文件

当文件访问完毕后,不再需要访问该文件时,需要及时的调用 file 对象的 close() 方法关闭文件。以下是及时关闭文件的例子:

file = open('test.txt')
file.read()
file.close()

2. 文件对象

open 返回一个 file 对象,通过调用 file 对象的成员方法访问该文件,下表总结了 file 对象的成员方法。

成员方法 功能
close() 关闭文件,关闭之后便不能再进行写入
write(string) 将字符串 string 写入文件
read(count) 从文件中读取一个字符串,至多读取 count 个字符
tell() 获取文件的当前访问位置
seek(offset, from) 改变文件的当前访问位置

seek(offset, from) 的功能是根据参数 offset 和 from 改变当前文件的访问位置:

  • 参数 offset 表示要移动的字节数
  • 参数 from 指定开始移动字节的参考位置
    • 如果 from 被设为 0,将文件的开头作为移动的参考位置
    • 如果 from 被设为 1,将文件的当前访问位置作为移动的参考位置
    • 如果 from 被设为 2,将文件的末尾作为移动的参考位置

3. 应用场景

3.1 使用 while 循环打印文件的每行

假设文件 test.txt 的内容:

www
imooc
com

下面使用 readline() 方读取文件的每行并打印:

file = open('test.txt')
while True:
    line = file.readline()
    if line == '':
        break
    print(line, end = '')
file.close()
  • 在第 1 行,以只读方式打开文件 test.txt
  • 在第 3 行,调用 readline() 方法读取文件的一行,读取的数据包括换行
  • 在第 4 行,如果读取到文件尾部,则返回一个空字符串,此时退出循环
  • 在第 6 行,缺省情况下 print 输出时会自动加上换行,因为 readline() 返回的数据会包含换行,因此使用命名参数 end = ‘’ 使 print 输出时不换行

运行程序,程序输出如下:

www
imooc
com

3.2 使用 for 循环打印文件的每行

Python 中的 file 对象是一个可迭代对象 Iterable,可以使用 for 循环遍历 file 对象。for 循环遍历文件的每一行,示例如下:

file = open('test.txt')
for line in file:
    print(line, end = '')
file.close()
  • 在第 2 行,使用 for 循环遍历文件 file 的每一行
    • line 指向当前遍历的行,包括换行符
  • 在第 4 行, 因为 line 包含换行符,因此使用命名参数 end = ‘’ 使 print 输出时不换行

3.3 复制文件

下面的例子实现复制文件的功能:

def copy(src_path, dst_path):
    src_file = open(src_path, 'r')
    dst_file = open(dst_path, 'w')
    for line in src_file:
        dst_file.write(line)
    src_file.close()
    dst_file.close()

copy('test.txt', 'test.bak')
  • 在第 2 行,以只读方式打开源文件 src_path
  • 在第 3 行,以只写方式打开目标文件 dst_path
  • 在第 4 行,遍历源文件 src_file 的每一行 line
  • 在第 5 行,将 line 写入到目标文件 dst_file
  • 在第 6 行,及时关闭 src_file 和 dst_file

3.4 向文件追加内容

编写一个用于记录日志的函数 log(msg),该函数将 msg 写入到日志文件 log.txt 中。每次写日志文件时,需要:

  • 保留日志文件的原有内容
  • 将新的记录添加到日志文件的尾部

文件 log.py 的内容如下:

from datetime import datetime

def log(msg):
    dt = datetime.today()
    now = dt.strftime("%Y-%m-%d %H:%M:%S")
    line = '%s: %s\n' % (now, msg)
    log_file.write(line)

log_file = open('log.txt', 'a')
log('hello')
log('world')
log_file.close()
  • 在第 3 行,编写函数 log(msg),用于将 msg 追加到日志文件 log.txt
    • 在第 4 行到第 5 行,获取当前时间 now
    • 在第 6 行,将当前时间 now 和信息 msg 追加到日志文件
  • 在第 9 行,以追加模式 a 打开文件 log.txt
    • 向文件写数据时,数据会被追加到文件尾部
  • 在第 10 行,向日志中写入 hello
  • 在第 11 行,向日志中写入 world
  • 在第 12 行,关闭日志文件

运行 log.py,命令如下:

C:\> python log.py
C:\> dir
2020/06/04  11:47    <DIR>          .
2020/06/04  11:47    <DIR>          ..
2020/06/04  11:49               249 log.py
2020/06/04  11:50                56 log.txt
  • 在第 6 行,显示当前目录下存在文件 log.txt
    • 以追加模式 a 打开文件 log.txt,如果文件不存在则创建文件

此时文件 log.txt 的内容如下,文件中包含有两行文本:

2020-06-04 11:49:54: hello
2020-06-04 11:49:54: world

再次运行程序 log.py,命令如下:

C:\> python log.py

此时文件 log.txt 的内容如下,文件中包含有四行文本:

2020-06-04 11:49:54: hello
2020-06-04 11:49:54: world
2020-06-04 11:50:06: hello
2020-06-04 11:50:06: world
  • 第 1 行和第 2 行,是第一次运行 log.py 产生的记录
  • 第 3 行和第 4 行,是第二次运行 log.py 产生的记录