文件模式详解:r、w、a、r+、w+、a+ 模式的区别与使用场景
说实话,刚学文件操作那会儿,我也被这六个模式搞得晕头转向。r、w、a 还好理解,一加上加号就懵了。今天咱们就把它们彻底捋清楚。
先看一张图,心里有个谱
下面这张 SVG 图,是我自己画的知识结构。你看完它,再往下读,会轻松很多。
基础模式:r、w、a
这三个模式,是文件操作的「三原色」。搞懂它们,加号模式自然就通了。
r 模式(只读)
这个最简单。打开文件只能读,不能写。文件不存在就报错。我平时读配置文件、日志文件,都用它。
# 读取一个文本文件
with open('config.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content)
注意:文件不存在时,会抛出 FileNotFoundError。我曾经在自动化脚本里忘了检查文件是否存在,结果半夜报警,爬起来一看,原来是路径写错了。
w 模式(只写)
这个模式有点「暴力」。它会先清空文件内容,再写入。如果文件不存在,它会新建一个。说白了,就是「从头来过」。
# 写入新内容,旧内容会被清空
with open('output.txt', 'w', encoding='utf-8') as f:
f.write('这是新内容')
我的习惯:每次用 w 模式前,我都会确认一下是不是真的要覆盖。尤其是操作生产环境的文件时,我会先备份。
a 模式(追加)
这个模式很「温柔」。它不会清空文件,而是把新内容写在文件末尾。文件不存在时也会新建。我常用它来写日志。
# 追加内容到文件末尾
with open('log.txt', 'a', encoding='utf-8') as f:
f.write('2024-01-01: 用户登录成功\n')
你想想看,如果日志用 w 模式,每次启动程序就把之前的日志全清了,那还查什么历史记录?
增强模式:r+、w+、a+
加号的意思是「加上写权限」。但具体怎么加,每个模式都不一样。
| 模式 | 文件必须存在? | 是否清空? | 指针初始位置 | 典型场景 |
|---|---|---|---|---|
| r+ | 是 | 否 | 文件开头 | 修改文件中的某一部分 |
| w+ | 否 | 是 | 文件开头(空文件) | 读写新文件 |
| a+ | 否 | 否 | 文件末尾 | 读取并追加日志 |
r+ 模式(读写,不覆盖)
这个模式有点「讲究」。它不会清空文件,你可以读,也可以写。但写的时候,是从当前指针位置开始覆盖。嗯,这里要注意:如果你先读后写,指针已经跑到末尾了,写的内容就追加到末尾了。
# 修改文件中的前几个字符
with open('data.txt', 'r+', encoding='utf-8') as f:
content = f.read() # 读完,指针在末尾
f.seek(0) # 把指针移回开头
f.write('新内容') # 从开头覆盖写入
避坑指南:我曾经用 r+ 模式想修改文件中间的内容,结果忘了移动指针,直接写到了末尾。后来我养成了习惯:用 r+ 时,一定先用 seek() 定位。
w+ 模式(读写,清空)
这个模式就是 w 的升级版。它会清空文件,然后你可以读写。但说实话,我很少用 w+。因为既然都清空了,读出来的也是空内容,还不如直接用 w 写,需要读的时候再单独打开。
# w+ 模式:先清空,再写入,最后读取
with open('temp.txt', 'w+', encoding='utf-8') as f:
f.write('临时数据')
f.seek(0) # 写完后指针在末尾,需要移回开头才能读
print(f.read())
我个人习惯,只有在需要「先写后读」的临时文件场景下,才会用 w+。
a+ 模式(读写,追加)
这个模式很有意思。它不会清空文件,写的时候永远在末尾追加。但读的时候,你可以通过 seek() 移动到任意位置读取。不过要注意,写操作不受 seek() 影响,永远追加在末尾。
# a+ 模式:先读取全部内容,再追加新内容
with open('history.log', 'a+', encoding='utf-8') as f:
f.seek(0) # 移到开头才能读取
old_content = f.read()
print('历史内容:', old_content)
f.write('新记录\n') # 自动追加到末尾
小技巧:用 a+ 模式时,如果想读取最新写入的内容,记得先 flush() 或 close() 再打开。因为写入的内容可能还在缓冲区里。
如何选择?我的经验总结
- 只是读文件 → 用 r。简单、安全,不会误写。
- 从头写一个新文件 → 用 w。省心,自动创建。
- 追加日志、记录 → 用 a。不会丢失历史数据。
- 需要修改文件中的某部分 → 用 r+。但记得用 seek() 控制指针。
- 先写后读的临时文件 → 用 w+。虽然场景不多,但确实有用。
- 既要追加又要读取历史 → 用 a+。注意指针位置。
最后说一句:文件操作看似简单,但坑不少。尤其是指针位置,稍不注意就出 bug。我建议你写代码时,多打印一下 tell() 看看指针在哪,心里就有数了。