今天你 AI 赋能了吗?打开浏览器,在地址栏输入 xuefulu.ai ,按下 Enter 即可访问学府路1号!
2026年10月14日 星期三 10:40:27
常用网址: | | 学府路1号 https://xuefulu.ai | 下载 Python https://xuefulu.ai/Python.html | 下载 TRAE https://xuefulu.ai/Trae.html

一、为什么要把数据存进文件

前面学的变量、列表,程序一关就没了。想让数据下次开机还在——比如全班的成绩、你写的日记、程序跑出来的日志——就得存进文件。

Python
# 变量存在内存里,程序关掉就消失
scores = [88, 72, 95, 60]
print('本次运行算好了,但关掉就没了')

把数据写进文件,就像把作业写进本子:合上本子,字迹还在。下次打开还能看。

存哪关掉程序后还在吗适合什么
变量 / 列表(内存)不在本次运行临时用
文件(硬盘)在要长期保存、下次还要用的数据
一句话记住:「这次用完就扔」放变量,「以后还要看」写文件。本课学的 open() 就是打开文件、读写内容的那把钥匙。

课程思政|诚信与职业操守 · 数据要有据可查

变量和列表住在内存里,程序一关就烟消云散;写进文件,数据才留得下来、下次还能复盘。做经济统计尤其要有这个意识:留痕不是多事,是让结论站得住脚。原始数据及时落盘、整理过程可以追溯,将来别人质疑你的结果,你能一条条翻出来讲清楚,这份底气就是职业操守给的。

二、open() 打开文件

读写文件的第一步是 open():把「文件名」交给它,它返回一个文件对象,后面读写都靠这个对象。

Python
with open('scores.txt', 'w', encoding='utf-8') as f:
    f.write('88\n72\n95\n60\n')
f = open('scores.txt', 'r', encoding='utf-8')
print(type(f))
f.close()

输出 <class '_io.TextIOWrapper'>(不用背这个名字,知道它是「文件对象」就行)。读完记得 close() 关掉。(这里故意用了一次老写法,让你看看文件对象的长相;close() 的讲究第五节马上讲,平时统一用 with 就行。)

三个最常用的参数

参数含义例子
第 1 个文件名(或路径)'scores.txt'
第 2 个模式:'r' 读 / 'w' 写 / 'a' 追加'r'
encoding编码,中文几乎总是写 'utf-8'encoding='utf-8'

三种模式

模式作用文件不存在时已存在时
'r'只读报错原样读
'w'写入(会清空)自动新建原内容被清空
'a'追加到末尾自动新建在末尾接着写,不清空
中文文件一定要写 encoding='utf-8'。不写的话,Python 就按「系统默认编码」处理:从 Python 3.15.0 起,默认编码已统一为 utf-8;更早的版本(尤其 Windows 上的)默认用 gbk,读你写的 utf-8 文件就可能乱码(详见第十节)。养成习惯:每次 open 都带 encoding。
模式可以组合,比如 'rb' 是「二进制读」、'w+' 是「读写」。起步阶段先把 'r' / 'w' / 'a' 三个用熟。

三、读文件的三种方式

拿到读模式('r')的文件对象后,有三种读法。

1. read():一口气读完整篇

Python
with open('scores.txt', 'w', encoding='utf-8') as f:
    f.write('88' + '\n' + '72' + '\n' + '95' + '\n' + '60' + '\n')
with open('scores.txt', 'r', encoding='utf-8') as f:
    content = f.read()
    print(content)

输出:

输出
88
72
95
60

read() 返回一个字符串,包含所有内容(含换行符)。

2. readlines():按行读成列表

Python
with open('scores.txt', 'w', encoding='utf-8') as f:
    f.write('88' + '\n' + '72' + '\n' + '95' + '\n')
with open('scores.txt', 'r', encoding='utf-8') as f:
    lines = f.readlines()
    print(lines)

输出 ['88\n', '72\n', '95\n']。每行是列表里的一个元素,行尾的换行符也带着。

3. 逐行 for:一行一行处理

Python
with open('scores.txt', 'w', encoding='utf-8') as f:
    f.write('88' + '\n' + '72' + '\n')
with open('scores.txt', 'r', encoding='utf-8') as f:
    for line in f:
        print(line.strip())   # strip() 去掉首尾空白和换行

输出:

输出
88
72
三选一怎么选?文件小、想整体处理用 read();要数行数或按行取用 readlines();文件很大、一行行处理用 for line in f(最省内存)。

四、写文件

写模式('w')用 write() 把字符串写进去。注意:write 只收字符串,不自动换行。

Python
with open('diary.txt', 'w', encoding='utf-8') as f:
    f.write('2026-09-21 今天学了 open()' + '\n')
print('写好了')

输出 写好了。文件里多了一行日记(' ' 是换行符,让每次写占一行)。

'w' 会清空原内容

同一个文件用 'w' 再开一次,里面的旧东西就没了:

Python
with open('e.txt', 'w', encoding='utf-8') as f:
    f.write('旧内容')
with open('e.txt', 'w', encoding='utf-8') as f:   # 又一次 w,原内容被清空
    f.write('新内容')
with open('e.txt', 'r', encoding='utf-8') as f:
    print(f.read())

输出 新内容。第一次写的「旧内容」已经被第二次 'w' 冲掉了。

'a' 是追加,不清空

Python
with open('log.txt', 'a', encoding='utf-8') as f:
    f.write('2026-09-21 登录' + '\n')
with open('log.txt', 'a', encoding='utf-8') as f:
    f.write('2026-09-21 退出' + '\n')
with open('log.txt', 'r', encoding='utf-8') as f:
    print(f.read())

输出:

输出
2026-09-21 登录
2026-09-21 退出

两次都用 'a',内容叠在后面,不会互相覆盖。日志、记录类文件就用它。

想保留老内容又想加新的,千万别用 'w',用 'a'。一个 'w' 下去,之前写的所有内容瞬间消失,且找不回来。

课程思政|数据安全与法治素养 · 按下保存前,先备份

'w' 模式一开,文件里的旧内容瞬间清零,而且找不回来——很多事故不是程序写错,是一个模式参数就抹掉了整份数据。覆盖之前,先留一份备份。真实的统计、财务数据往往不可再生,改之前先另存、删之前先确认,这不是胆小,是数据工作者的基本责任。

五、关文件与 with open

文件用完后要 close() 关掉,否则内容可能没真正写进硬盘(存在缓冲区里)。

老写法:手动 close

Python
f = open('a.txt', 'w', encoding='utf-8')
f.write('hello')
f.close()        # 必须记得关

推荐写法:with open(自动关)

Python
with open('a.txt', 'w', encoding='utf-8') as f:
    f.write('hello')
# 离开这个缩进块,文件自动关闭,不用写 close()

两种写法效果一样,但 with 会在代码块结束时自动关闭文件,就算中间出错了也会关。新手强烈推荐 with。

手动 closewith open
会不会忘关容易忘不会,自动关
中途报错可能没关上照样关好
代码量多一行 close少一行,更干净
记住这个最常用模板,直接抄:
with open('文件名', 'r', encoding='utf-8') as f:
内容 = f.read()
读和写都把操作写在缩进块里即可。

六、路径怎么写

open() 第一个参数是「文件在哪」。有两种写法:

写法含义例子
相对路径相对「你运行代码时的目录」'scores.txt'(当前目录)
绝对路径从根目录写全'/tmp/tut_demo/scores.txt'

相对路径最省事:写 'scores.txt',文件就创建在你运行代码的那个文件夹里。本篇所有例子都用相对路径,方便你照抄。

Windows 上的反斜杠坑

Windows 路径用反斜杠 \,而反斜杠在 Python 字符串里是转义符。所以要写成两个反斜杠或原始字符串 r"":

Python
# 示意:Windows 上的正确写法
open('C:\\Users\\笔记\\data.txt')
open(r'C:\Users\笔记\data.txt')

在 Mac / Linux 上路径用正斜杠 /,不用操心这个。教学示例都在 /tmp/tut_demo/ 下验证,与系统无关。

在 Windows 上写 'C:\Users\name.txt' 只写一个反斜杠会报错或路径错乱——要么写两个 \\,要么在整个字符串前加 r 变成原始字符串。

七、文件不存在会怎样

用 'r' 读一个不存在的文件,Python 会直接报错:

Python
with open('不存在.txt', 'r', encoding='utf-8') as f:
    print(f.read())
报错
FileNotFoundError: [Errno 2] No such file or directory: '不存在.txt'

原因:要读的文件根本没创建过。先确认文件名、路径拼写对不对。

但 'w' 和 'a' 会帮你自动创建

写模式和追加模式很「友好」:文件不在,它直接帮你建一个。

Python
import os
print(os.path.exists('newf.txt'))
with open('newf.txt', 'w', encoding='utf-8') as f:
    f.write('创建我')
print(os.path.exists('newf.txt'))

输出:

输出
False
True

第一次打印 False(还不存在),写完再打印就 True 了。

判断文件在不在,用 os.path.exists('文件名')(别忘了先 import os)。读之前先判断,能避免一大半的 FileNotFoundError。

AI 助手小贴士|with 的价值要用「不用它会怎样」来衬托。

打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:with open(...) as f 到底帮我关了什么?不用 with 且忘了 close 会出什么问题?给两个对照的小例子

课程思政|工匠精神与严谨求实 · 动手之前,先看一眼

读文件前用 os.path.exists() 问一句「它在不在」,就能躲开一大半 FileNotFoundError。小事一桩,却是好习惯:先确认前提,再动手。不臆断、不硬闯,把「我以为它有」换成「我确认真有」,这份谨慎挪到写论文、做报表上,就是让人放心的严谨。

八、配合异常处理读文件

第七节的报错可以用 try...except 接住,让程序「文件不在就提示,而不是直接崩」:

Python
try:
    with open('config.txt', 'r', encoding='utf-8') as f:
        print(f.read())
except FileNotFoundError:
    print('配置文件不存在,使用默认设置')

把 config.txt 换成不存在的文件运行,输出就是 配置文件不存在,使用默认设置,程序稳稳继续。

场景不处理用 try...except 处理
读不存在的文件程序崩溃报错给出友好提示,继续运行
读用户指定的文件用户手滑就崩提示「文件不存在」让用户重来
读「别人给的、可能不存在」的文件时,套一层 try...except FileNotFoundError 是好习惯。异常处理的完整讲法见《25-异常处理》,这里先会用这一种。

九、十个基础案例

下面十个例子都很短,建议每个都亲手敲一遍、跑一遍。它们覆盖了文件操作最常见的用法。

1. 写一条购物清单

Python
with open('购物清单.txt', 'w', encoding='utf-8') as f:
    f.write('牛奶 2 盒' + '\n')
print('清单已保存')

输出 清单已保存。

2. 读购物清单

Python
with open('购物清单.txt', 'w', encoding='utf-8') as f:
    f.write('牛奶 2 盒' + '\n' + '面包 1 袋' + '\n' + '鸡蛋 10 个' + '\n' + '酸奶 3 瓶' + '\n')
with open('购物清单.txt', 'r', encoding='utf-8') as f:
    print(f.read())

输出:

输出
牛奶 2 盒
面包 1 袋
鸡蛋 10 个
酸奶 3 瓶

3. 数一数清单有几样

Python
with open('购物清单.txt', 'w', encoding='utf-8') as f:
    f.write('牛奶 2 盒' + '\n' + '面包 1 袋' + '\n' + '鸡蛋 10 个' + '\n' + '酸奶 3 瓶' + '\n')
with open('购物清单.txt', 'r', encoding='utf-8') as f:
    print(len(f.readlines()))

输出 4。

4. 追加一条

Python
with open('追加清单.txt', 'a', encoding='utf-8') as f:
    f.write('牛奶 2 盒' + '\n')
with open('追加清单.txt', 'a', encoding='utf-8') as f:
    f.write('纸巾 1 提' + '\n')
with open('追加清单.txt', 'r', encoding='utf-8') as f:
    print(f.read())

输出:

输出
牛奶 2 盒
纸巾 1 提

5. 复制清单

Python
with open('清单A.txt', 'w', encoding='utf-8') as f:
    f.write('奶茶')
with open('清单A.txt', 'r', encoding='utf-8') as s, open('清单B.txt', 'w', encoding='utf-8') as d:
    d.write(s.read())
with open('清单B.txt', 'r', encoding='utf-8') as f:
    print(f.read())

输出 奶茶。

6. 统计口味出现次数

Python
with open('口味.txt', 'w', encoding='utf-8') as f:
    f.write('珍珠 椰果 珍珠 布丁 椰果 珍珠')
from collections import Counter
with open('口味.txt', 'r', encoding='utf-8') as f:
    print(Counter(f.read().split()))

输出 Counter({'珍珠': 3, '椰果': 2, '布丁': 1})。

7. 逐行读价格求总价

Python
with open('价格.txt', 'w', encoding='utf-8') as f:
    f.write('12' + '\n' + '8' + '\n' + '15' + '\n' + '20' + '\n')
total = 0
with open('价格.txt', 'r', encoding='utf-8') as f:
    for line in f:
        total += int(line)
print(total)

输出 55。

8. 一次写多行

Python
lines = ['牛奶 2 盒', '面包 1 袋', '鸡蛋 10 个']
with open('多行清单.txt', 'w', encoding='utf-8') as f:
    for ln in lines:
        f.write(ln + '\n')
with open('多行清单.txt', 'r', encoding='utf-8') as f:
    print(f.read())

输出:

输出
牛奶 2 盒
面包 1 袋
鸡蛋 10 个

9. 判断文件是否存在

Python
import os
print(os.path.exists('存在检查.txt'))
with open('存在检查.txt', 'w', encoding='utf-8') as f:
    f.write('x')
print(os.path.exists('存在检查.txt'))

输出:

输出
False
True

10. 把总价存成文件

Python
r = sum([12, 8, 15, 20])
with open('总价.txt', 'w', encoding='utf-8') as f:
    f.write(str(r))
with open('总价.txt', 'r', encoding='utf-8') as f:
    print(f.read())

输出 55。

十、常见坑

坑 1:忘了 close(或没用 with)

不用 with 又忘写 close(),写进去的内容可能卡在缓冲区、没真正落盘。直接养成用 with 的习惯最保险。

坑 2:用 'w' 把原文件清空了

想追加却写成 'w',老数据瞬间没了。要保留用 'a'。验证一下:

Python
with open('e.txt', 'w', encoding='utf-8') as f:
    f.write('旧内容')
with open('e.txt', 'w', encoding='utf-8') as f:
    f.write('新内容')
with open('e.txt', 'r', encoding='utf-8') as f:
    print(f.read())

输出 新内容——「旧内容」已被覆盖。

坑 3:编码不一致,读出乱码

写的时候用一种编码、读的时候用另一种,屏幕就会出现看不懂的符号:

Python
with open('g.txt', 'w', encoding='gbk') as f:
    f.write('中文')
with open('g.txt', 'r', encoding='latin-1') as f:
    print(f.read())

输出 ÖÐÎÄ——这就是「乱码」的样子。两边编码对不上,同样的字节被解读成了别的字。

杜绝乱码的办法就一句:写和读都用同一个编码,统一 encoding='utf-8'。从 Python 3.15.0 起,不写 encoding 时默认也是 utf-8;但更早的版本在 Windows 上默认用 gbk,拿到你文件的电脑装的是什么版本谁也说不准——显式写上,到哪都不乱。
补充:不写 encoding,Python 到底用什么编码?取决于版本——2026 年 10 月发布的 Python 3.15.0 已经把默认编码统一成 utf-8,读写文本文件不再「看系统脸色」;3.15.0 之前的版本则是跟着系统走:Mac / Linux 默认 utf-8,Windows 默认 gbk。拿 python --version 看一眼自己电脑装的是哪个版本;不管新版旧版,显式写上 encoding='utf-8' 都是最稳妥的习惯——你写的文件总要发给别人、也要在自己换电脑后还能打开。

坑 4:read() 调了两次,第二次是空

文件对象有个「读指针」,read() 读完后指针到了末尾,再读就什么都没有:

Python
with open('t.txt', 'w', encoding='utf-8') as f:
    f.write('hello')
with open('t.txt', 'r', encoding='utf-8') as f:
    print(f.read())
    print(f.read())

输出:

输出
hello

第一次打印 hello,第二次打印一个空行。想再读,要么重新 open,要么先 f.seek(0) 把指针拉回开头。

坑 5:write() 直接写数字

write() 只收字符串,传数字进去会报错:

Python
with open('num.txt', 'w', encoding='utf-8') as f:
    f.write(100)        # 报错:write 只收字符串
报错
TypeError: write() argument must be str, not int

原因:数字要先 str(100) 转成字符串再写。比如 f.write(str(总分))。

五个坑一句话记:用 with 自动关、追加用 a 别用 w、编码统一 utf-8、read 第二次是空、写之前 str() 转字符串。

十一、报错手册

文件相关最常见的报错就这两条,照抄本机解释器原文。

Python
with open('不存在.txt', 'r', encoding='utf-8') as f:
    print(f.read())
报错
FileNotFoundError: [Errno 2] No such file or directory: '不存在.txt'

原因:用 'r' 读一个不存在的文件。检查文件名/路径拼写,或先 os.path.exists() 判断,或改用 try...except 接住。

Python
with open('num.txt', 'w', encoding='utf-8') as f:
    f.write(100)
报错
TypeError: write() argument must be str, not int

原因:write() 只收字符串。数字用 str(100) 转一下再写。

Python
with open('g.txt', 'w', encoding='utf-8') as f:
    f.write('中文')
with open('g.txt', 'r', encoding='gbk') as f:
    print(f.read())
报错
UnicodeDecodeError: 'gbk' codec can't decode byte 0xad in position 2: illegal multibyte sequence

原因:写是 utf-8、读却用 gbk,编码对不上。统一成 encoding='utf-8' 即可。

AI 助手小贴士|读和写都学完了,来一个完整的小闭环。

打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:写一个完整小程序:读 scores.csv 统计每科平均分,再把结果写入 result.txt。逐行加中文注释,我要照着敲一遍

十二、文件操作小结

把前面所有要点压缩成一张「操作清单」,写文件前扫一遍:

要做的事怎么写提醒
读文件open('x.txt', 'r', encoding='utf-8')文件要有
写文件(覆盖)open('x.txt', 'w', encoding='utf-8')会清空旧的
追加写open('x.txt', 'a', encoding='utf-8')不清空旧的
自动关文件用 with open(...) as f:不用写 close
写数字f.write(str(n))先转字符串
判断存在os.path.exists('x.txt')先 import os

一个综合小例子

把「一批分数求平均,结果存进文件」串起来:

Python
import os
scores = [88, 72, 95, 60]
avg = sum(scores) / len(scores)
with open('avg.txt', 'w', encoding='utf-8') as f:
    f.write('平均分是 ' + str(avg))
print('已保存,文件存在吗:', os.path.exists('avg.txt'))

输出 已保存,文件存在吗: True。

文件操作的套路永远是三步:open 打开 → 读写 → 关掉(用 with 自动关)。把这三步和「编码统一 utf-8、写前 str()」记住,文件题就稳了。

AI 助手小贴士|文件操作的报错八成和路径、编码有关。让 AI 按「可能性从高到低」帮你排查。

打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:我用 open() 打开 txt 文件报 UnicodeDecodeError。请列出可能的原因,分别给出排查方法,并解释 encoding="utf-8" 参数的作用

十三、随堂自测(10 题)

点击选项即可判分,选错的题会立刻展开解析。做完可以看到正确率和错题清单,也可以重新答题。

已答 0 / 10 · 正确 0 —

1打开一个文件时,为了中文不乱码,第三个常用参数通常写成?

  • A. encoding='utf-8'
  • B. mode='text'
  • C. code='cn'
  • D. 不需要写,任何版本都默认对
答案:A。中文文件几乎总是显式写 encoding='utf-8'。不写时的默认编码取决于版本:从 Python 3.15.0 起已统一为 utf-8,更早的版本在 Windows 上默认 gbk——写上 encoding,换哪台电脑都不会乱码。

2用 open('x.txt', 'w') 打开一个已有文件再写,会发生什么?

  • A. 在末尾追加新内容
  • B. 原内容被清空,只留新写的
  • C. 直接报错不让写
  • D. 什么都不做
答案:B。'w' 是「写入模式」,打开的瞬间就把原内容清空。想保留老内容要用 'a' 追加。

3想「在文件末尾接着写、不覆盖原来的内容」,模式应该选?

  • A. 'r'
  • B. 'w'
  • C. 'x'
  • D. 'a'
答案:D。'a' 是 append(追加),新内容叠在老内容后面。日志、记录类文件都用它。

4为什么大家都推荐用 with open(...) as f: 而不是直接 open 后手动 close()?

  • A. with 运行更快
  • B. with 会在块结束时自动关文件,不容易忘
  • C. with 不用写文件名
  • D. 两种方法完全一样
答案:B。with 离开缩进块自动 close(),就算中途出错也会关,省心且不容易丢数据。

5f.read() 一口气读完整篇文件,返回的是什么类型?

  • A. 列表 list
  • B. 字典 dict
  • C. 字符串 str
  • D. 数字 int
答案:C。read() 返回字符串(含换行符)。想按行成列表用 readlines(),想逐行处理用 for line in f。

6用 'r' 模式去读一个根本不存在的文件,会报什么错?

  • A. ValueError
  • B. TypeError
  • C. FileNotFoundError
  • D. PermissionError
答案:C。读不存在的文件抛 FileNotFoundError。先核对文件名/路径,或用 os.path.exists()、try...except 提前处理。

7f.write(100) 这样直接把一个整数写进文件,会怎样?

  • A. 正常写进 100
  • B. 写成字符串 '100'
  • C. 报错,write 只收字符串
  • D. 写入 0
答案:C。write() 只收字符串,直接传数字会报 TypeError。要先 str(100) 转成字符串再写。

8对同一个文件对象连续调用两次 f.read(),第二次会怎样?

  • A. 再读一遍同样的内容
  • B. 从头再读
  • C. 得到一个空字符串
  • D. 直接报错
答案:A。文件对象有「读指针」,第一次 read() 读完指针到了末尾,第二次读到的就是空字符串。要重读得重新 open 或 f.seek(0)。

9代码里写 open('data.txt') 这种相对路径,「当前目录」指的是?

  • A. 文件所在的目录
  • B. 你运行这段代码的目录
  • C. C 盘根目录
  • D. 桌面
答案:B。相对路径相对于运行代码时的目录。在本篇里,示例都相对你运行 Python 的文件夹,我们用 /tmp/tut_demo/ 验证。

10有一段重要日志不想被覆盖,应该用哪种模式打开日志文件?

  • A. 'w'
  • B. 'r'
  • C. 'rb'
  • D. 'a'
答案:D。'a' 是追加,新日志叠在后面;'w' 会清空旧日志。重要数据务必用 'a'。