文件模式详解:r、w、a、r+、w+、a+ 模式的区别与使用场景

说实话,刚学文件操作那会儿,我也被这六种模式搞得晕头转向。r、w、a 还好理解,一加上 + 号就乱了。后来我在一个日志系统项目里踩了坑,才真正把这些模式吃透。

今天我就带你把这六种模式掰开揉碎,讲清楚。你想想看,搞懂了它们,以后写文件代码基本不会翻车。

一、先看一张图,心里有个谱

下面这张 SVG 图,是我自己画的。它把六种模式的核心逻辑串起来了。你看完这张图,再往下读,会轻松很多。

Python 文件模式核心逻辑图 文件对象 只读模式 只写模式 追加模式 r r+ w w+ a a+ 关键行为差异 r / r+ : 文件必须存在,否则报错。指针在开头。 w / w+ : 文件不存在则创建,存在则清空。指针在开头。 a / a+ : 文件不存在则创建,存在则追加。指针在末尾。

二、三种基础模式:r、w、a

这三种模式,是文件操作的「三原色」。其他带 + 号的,都是它们的变体。

1. r 模式(只读)

说白了就是:只能读,不能写。文件必须存在,否则直接报错 FileNotFoundError。

我个人习惯用 r 模式来读取配置文件、日志文件、数据文件。因为它最安全——你不可能手滑把文件内容改了。

适用场景:读取日志、加载配置、读取 CSV/JSON 数据文件。
# r 模式示例
with open('config.txt', 'r', encoding='utf-8') as f:
    content = f.read()
    print(content)
# 如果文件不存在,这里会直接崩溃

2. w 模式(只写)

注意了:w 模式会清空文件!不管你原来文件里有什么,一打开就全没了。

我曾经在写一个数据导出工具时,不小心用了 w 模式打开了一个重要的配置文件……嗯,那次教训挺深刻的。后来我养成了一个习惯:用 w 模式前,先确认文件是不是真的该被覆盖。

⚠️ 重要提醒:w 模式会清空原文件内容。如果文件不存在,它会自动创建一个新文件。
# w 模式示例
with open('output.txt', 'w', encoding='utf-8') as f:
    f.write('这是新写入的内容\n')
# 原文件内容全部丢失

3. a 模式(追加)

a 模式不会清空文件。它把指针放在文件末尾,你写的内容会追加到后面。

我特别喜欢用 a 模式写日志。你想想看,日志文件本来就是越写越长,谁也不想每次启动程序就把历史日志清空吧?

小技巧:a 模式如果文件不存在,也会自动创建。这一点和 w 模式一样,但行为完全不同。
# a 模式示例
with open('app.log', 'a', encoding='utf-8') as f:
    f.write('2024-01-15 10:30:00 用户登录成功\n')
# 原有日志保留,新内容追加在末尾

三、三种增强模式:r+、w+、a+

带 + 号的模式,说白了就是「读写双修」。但它们的读写行为差异很大,我一个个讲。

4. r+ 模式(读写,文件必须存在)

r+ 模式不会清空文件。你可以读,也可以写。但写的时候,是从当前指针位置开始覆盖。

我在项目中遇到过这样一个场景:需要修改一个 JSON 文件的某个字段。用 r+ 模式打开,先读到内存里,修改完再 seek 回开头写入。嗯,这个模式很适合「原地修改」的场景。

适用场景:需要同时读写一个已有文件,且不想清空它。
# r+ 模式示例
with open('data.txt', 'r+', encoding='utf-8') as f:
    content = f.read()      # 先读
    f.seek(0)               # 指针回到开头
    f.write('新内容')       # 覆盖写入

5. w+ 模式(读写,会清空文件)

w+ 模式会清空文件。它和 w 模式一样,先把你原来的内容全删了,然后你才能读写。

说实话,我很少用 w+ 模式。因为如果你只是想写,用 w 就够了;如果你想读,文件都被清空了,读个啥?不过有一种情况例外:你需要在内存中模拟一个临时文件,先写后读。

⚠️ 注意:w+ 模式会清空文件。如果你需要保留原内容,请用 r+ 或 a+。
# w+ 模式示例
with open('temp.txt', 'w+', encoding='utf-8') as f:
    f.write('先写入一些内容\n')
    f.seek(0)
    print(f.read())  # 读取自己刚写的内容

6. a+ 模式(读写追加,不会清空)

a+ 模式是我个人最喜欢的「安全模式」。它不会清空文件,而且写的时候永远追加到末尾。读的时候,你需要手动把指针移到开头。

我曾经用 a+ 模式写过一个简单的聊天记录工具。每次收到消息就追加写入,用户想查看历史记录时,就把指针移到开头读取全部内容。完美。

小技巧:a+ 模式下,写操作永远在文件末尾。即使你调用了 seek(0),写的时候还是会跳到末尾。这是它的特性,不是 bug。
# a+ 模式示例
with open('chat.log', 'a+', encoding='utf-8') as f:
    f.write('新消息:你好\n')
    f.seek(0)
    all_content = f.read()  # 读取全部历史记录

四、一张表总结所有模式

下面这张表,是我自己整理的。每次写文件操作前,我都会瞄一眼。建议你收藏。

模式 读 写 文件必须存在 是否清空 指针初始位置 典型场景
r ✅ ❌ ✅ 是 ❌ 否 开头 读取配置文件
w ❌ ✅ ❌ 否(自动创建) ✅ 是 开头 写入新文件
a ❌ ✅ ❌ 否(自动创建) ❌ 否 末尾 追加日志
r+ ✅ ✅ ✅ 是 ❌ 否 开头 修改已有文件
w+ ✅ ✅ ❌ 否(自动创建) ✅ 是 开头 临时文件读写
a+ ✅ ✅ ❌ 否(自动创建) ❌ 否 末尾 日志读写

五、避坑指南

最后,分享几个我踩过的坑。你记住了,以后能省不少时间。

  • 我曾经用 r+ 模式打开一个文件,读完之后直接写,结果发现写的内容覆盖了原来的内容,而不是追加。原因很简单:读完之后指针在末尾,写的时候就从末尾开始覆盖了。解决方案:用 seek(0) 回到开头,或者用 a+ 模式。
  • 我曾经在 w 模式下忘记备份原文件,结果数据全丢了。现在我的习惯是:用 w 模式前,先 copy 一份备份。
  • 我曾经以为 a+ 模式可以随意读写,结果发现写操作永远在末尾。想读的时候必须手动 seek(0)。这不是 bug,是设计如此。

好了,文件模式的六种模式,今天就讲到这里。你只要记住一句话:读用 r,写用 w,追加用 a,需要同时读写就加 +。 然后根据文件是否必须存在、是否允许清空,选对模式就行。

下次写文件操作时,不妨先问问自己:这个文件必须存在吗?允许清空吗?需要同时读写吗?三个问题问完,模式自然就选对了。