一、回顾与本篇目标
上一篇我们学了模块和包,你的代码可以拆分到多个文件里了。上一篇我们学的模块和包,主要是为了把代码组织好——函数放到不同的 .py 文件里,通过 import 引入使用。这些都发生在程序运行期间,数据存在变量里。程序运行期间,数据存在变量和容器里。一旦程序退出,所有数据都没了。
数据需要持久化。持久化的一种方式是用数据库,另一种更简单的方式是把数据写到硬盘上的文件里。Python 对文件读写的支持非常简洁,几行代码就能完成。如果你之前跟着《后端零基础入门》学过 Node.js 的 fs 模块,这一篇的很多概念你会觉得似曾相识——读写文件、JSON 序列化、CSV 处理,核心思想都一样,只是语法不同。
在很多实际场景里,文件读写比数据库更实用:处理日志文件、读取配置文件、导出 CSV 报表、把爬虫抓到的数据存成 JSON——这些都是在写文件。
本篇的目标:
- 学会用
open()打开和关闭文件 - 掌握读文件的三种方式:
read()、readline()、readlines() - 学会写文件和追加文件
- 理解
with语句的作用——自动关闭文件 - 学会 JSON 的序列化与反序列化
- 学会 CSV 的读写
二、打开和关闭文件:open() 和 close()
Python 用内置函数 open() 来打开文件。它返回一个文件对象,通过这个对象你可以读写文件的内容。
基本语法
文件对象 = open('文件路径', '模式', encoding='编码')
- 文件路径:文件在硬盘上的位置。可以是相对路径(
'data.txt')或绝对路径('C:/Users/xxx/data.txt')。 - 模式:告诉 Python 你要做什么——读、写、追加等。
- 编码:处理中文时一般指定
encoding='utf-8',否则在 Windows 上可能出现乱码。
文件打开模式
| 模式 | 含义 | 文件不存在时 | 文件存在时 |
|---|---|---|---|
'r' |
只读(默认) | 报错 | 从开头读取 |
'w' |
写入(覆盖) | 创建新文件 | 清空原内容 |
'a' |
追加 | 创建新文件 | 在末尾追加 |
'r+' |
读写 | 报错 | 从开头读写 |
'rb' |
二进制读 | 报错 | 从开头读取字节 |
'wb' |
二进制写 | 创建新文件 | 清空原内容 |
最常用的三个模式:'r' 读、'w' 写、'a' 追加。'w' 模式需要特别小心——它会清空文件原有内容。如果只是想添加内容,用 'a'。
关闭文件:close()
文件用完后必须关闭。如果不关,可能会导致:写入的内容没有真正保存到硬盘、占用系统文件句柄(打开太多文件会报错)。
# 打开文件
file = open('data.txt', 'r', encoding='utf-8')
# 读取内容(后面会讲)
content = file.read()
print(content)
# 关闭文件
file.close()
但每次都手动 close() 很麻烦,而且如果在 open() 和 close() 之间程序抛出了异常,close() 可能根本不会被执行。下一节会介绍更好的写法。
三、读取文件的三种方式
假设我们有一个文件 data.txt,内容如下:
第一行:你好,世界
第二行:Python 文件读写
第三行:坚持学习
方式一:read()——一次性读取整个文件
file = open('data.txt', 'r', encoding='utf-8')
content = file.read() # 把整个文件内容读成一个字符串
file.close()
print(content)
# 输出:
# 第一行:你好,世界
# 第二行:Python 文件读写
# 第三行:坚持学习
read() 可以传入一个数字参数,表示读取多少个字符:
file = open('data.txt', 'r', encoding='utf-8')
partial = file.read(10) # 只读前 10 个字符
print(partial) # '第一行:你好,世界\n第'
file.close()
方式二:readline()——一次读取一行
file = open('data.txt', 'r', encoding='utf-8')
line1 = file.readline() # 读取第一行(包括行尾的换行符)
line2 = file.readline() # 读取第二行
file.close()
print(repr(line1)) # '第一行:你好,世界\n'
print(repr(line2)) # '第二行:Python 文件读写\n'
readline() 返回的字符串末尾包含 \n(换行符)。如果你不想要换行符,可以用 strip() 去掉。
方式三:readlines()——一次读取所有行,返回列表
file = open('data.txt', 'r', encoding='utf-8')
lines = file.readlines() # 返回列表,每个元素是一行
file.close()
print(lines)
# ['第一行:你好,世界\n', '第二行:Python 文件读写\n', '第三行:坚持学习']
三种方式的适用场景:
- 文件很小:用
read()一次性读取,简单省事。 - 需要逐行处理:用
for循环直接遍历文件对象(最推荐的方式,见下一节)。 - 需要随机访问某一行:用
readlines()拿到所有行的列表,然后按索引访问。
最好的读文件方式:用 for 循环遍历文件对象
Python 的文件对象本身是可迭代的——你可以直接对它做 for 循环,一行一行地读,不需要先把整个文件加载到内存:
file = open('data.txt', 'r', encoding='utf-8')
for line in file:
line = line.strip() # 去掉换行符和空白
print(f'读到一行:{line}')
file.close()
这是处理大文件的最佳方式——每次只在内存中保存一行,不会撑爆内存。
四、with 语句:自动关闭文件的优雅写法
前面所有的例子都用了 open() + close()。但有一种更好的写法——with 语句。它会在代码块执行完毕后自动关闭文件,即使代码块内部抛出了异常也能保证关闭。
# 推荐写法:用 with,不需要手动 close()
with open('data.txt', 'r', encoding='utf-8') as file:
content = file.read()
print(content)
# 出了 with 块,文件自动关闭了
# 等价于之前的写法,但更安全
with 语句的本质 是 Python 的上下文管理器协议。你不需要理解底层原理,只需要记住:读写文件永远用 with 语句。后面的所有示例都会用这个写法。
五、写入文件
覆盖写入:’w’ 模式
# 写入新文件(如果文件不存在会自动创建)
with open('output.txt', 'w', encoding='utf-8') as file:
file.write('这是第一行\n')
file.write('这是第二行\n')
file.write('这是第三行\n')
print('写入完成!')
执行后,output.txt 的内容就是你写入的三行。注意:write() 不会自动加换行符,你需要手动加 \n。如果你再次用 'w' 模式打开这个文件并写入新内容,原来的内容会被清空。
追加写入:’a’ 模式
# 在文件末尾追加内容,不覆盖原有内容
with open('output.txt', 'a', encoding='utf-8') as file:
file.write('这是追加的一行\n')
file.write('这也是追加的一行\n')
写入多行:writelines()
lines = ['第一行\n', '第二行\n', '第三行\n']
with open('output.txt', 'w', encoding='utf-8') as file:
file.writelines(lines)
writelines() 接收一个字符串列表,把每个元素写入文件。和 write() 一样,它不会自动加换行符,你需要自己确保每个字符串末尾有 \n。
六、JSON 文件的读写
JSON 是一种通用的数据交换格式。Python 的 json 模块提供了四个核心函数:
| 函数 | 方向 | 作用 |
|---|---|---|
json.dumps(obj) |
Python → JSON 字符串 | 把 Python 对象转成 JSON 字符串 |
json.loads(str) |
JSON 字符串 → Python | 把 JSON 字符串转成 Python 对象 |
json.dump(obj, file) |
Python → JSON 文件 | 把 Python 对象写入文件 |
json.load(file) |
JSON 文件 → Python | 从文件读取并转成 Python 对象 |
记忆技巧:带 s 的是处理字符串(string),不带 s 的是处理文件。这跟 JavaScript 的 JSON.parse() 和 JSON.stringify() 类似。
把 Python 对象写入 JSON 文件
import json
# Python 数据
users = [
{'name': '张三', 'age': 28, 'city': '上海'},
{'name': '李四', 'age': 22, 'city': '北京'},
{'name': '王五', 'age': 25, 'city': '广州'}
]
# 写入 JSON 文件
with open('users.json', 'w', encoding='utf-8') as file:
json.dump(users, file, ensure_ascii=False, indent=2)
print('JSON 文件写入完成')
参数解释:
ensure_ascii=False:不把中文转成\uXXXX的形式,直接保留中文字符。不加这个参数的话,中文会变成乱码似的转义序列。indent=2:缩进 2 个空格,让 JSON 文件格式化好读。不加的话所有内容会挤在一行。
生成的 users.json 文件:
[
{
"name": "张三",
"age": 28,
"city": "上海"
},
{
"name": "李四",
"age": 22,
"city": "北京"
},
{
"name": "王五",
"age": 25,
"city": "广州"
}
]
从 JSON 文件读取数据
import json
with open('users.json', 'r', encoding='utf-8') as file:
users = json.load(file)
# users 现在是一个 Python 列表,包含三个字典
for user in users:
print(f'{user["name"]},{user["age"]}岁,来自{user["city"]}')
处理字符串格式的 JSON
import json
# JSON 字符串
json_str = '{"name": "张三", "age": 28}'
# 解析成 Python 对象
data = json.loads(json_str)
print(data['name']) # 张三
# 反过来:Python 对象转 JSON 字符串
json_str = json.dumps(data, ensure_ascii=False)
print(json_str) # {"name": "张三", "age": 28}
七、CSV 文件的读写
CSV(逗号分隔值)是一种表格数据格式,广泛用于 Excel 导出、数据分析等场景。Python 内置了 csv 模块。
写入 CSV 文件
import csv
# 数据:每行是一个列表
data = [
['姓名', '年龄', '城市'], # 表头
['张三', 28, '上海'],
['李四', 22, '北京'],
['王五', 25, '广州']
]
with open('users.csv', 'w', encoding='utf-8', newline='') as file:
writer = csv.writer(file)
# writerows 一次性写入多行
writer.writerows(data)
print('CSV 文件写入完成')
newline='' 这个参数在 Windows 上很重要。如果不加,写入的每一行之间会多出一个空行。
读取 CSV 文件
import csv
with open('users.csv', 'r', encoding='utf-8') as file:
reader = csv.reader(file)
for row in reader:
print(f'姓名:{row[0]},年龄:{row[1]},城市:{row[2]}')
用字典形式读写 CSV
如果 CSV 有表头,用 DictReader 和 DictWriter 会更直观:
import csv
# 用字典形式写入
data = [
{'姓名': '张三', '年龄': 28, '城市': '上海'},
{'姓名': '李四', '年龄': 22, '城市': '北京'},
]
with open('users_dict.csv', 'w', encoding='utf-8', newline='') as file:
fieldnames = ['姓名', '年龄', '城市']
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader() # 写入表头
writer.writerows(data) # 写入数据行
# 用字典形式读取
with open('users_dict.csv', 'r', encoding='utf-8') as file:
reader = csv.DictReader(file)
for row in reader:
# row 是一个字典,键是表头
print(f"{row['姓名']},{row['年龄']}岁")
DictReader 把每一行数据解析成字典,键是表头列名。这样访问字段不用记列索引,代码可读性更好。
八、文件与目录操作:os 模块
除了读写文件内容,还需要对文件本身进行操作——检查是否存在、重命名、删除等。这些功能在 os 模块中。
import os
# 检查文件是否存在
print(os.path.exists('data.txt')) # True 或 False
# 检查是否是文件/目录
print(os.path.isfile('data.txt')) # True
print(os.path.isdir('my_folder')) # True
# 获取文件大小(字节)
print(os.path.getsize('data.txt'))
# 重命名文件
os.rename('old_name.txt', 'new_name.txt')
# 删除文件
os.remove('temp.txt')
# 创建目录
os.makedirs('new_folder/sub_folder', exist_ok=True)
# exist_ok=True 表示如果目录已存在不报错
# 列出目录中的文件
files = os.listdir('.') # '.' 表示当前目录
for f in files:
print(f)
# 拼接路径(跨平台,推荐用这个而不是字符串拼接)
path = os.path.join('folder', 'subfolder', 'file.txt')
print(path) # Windows: folder\subfolder\file.txt Mac: folder/subfolder/file.txt
九、和 Node.js 文件读写的对比
如果你之前跟着后端入门系列学过 Node.js 的 fs 模块,这张对比表能帮你快速迁移:
| 操作 | Node.js | Python |
|---|---|---|
| 读文件(同步) | fs.readFileSync(path, 'utf-8') |
open(path, 'r').read() |
| 读文件(异步) | fs.readFile(path, callback) |
Python 一般是同步,异步需 aiofiles |
| 写文件 | fs.writeFileSync(path, data) |
open(path, 'w').write(data) |
| JSON 解析 | JSON.parse(str) / JSON.stringify(obj) |
json.loads(str) / json.dumps(obj) |
| 路径拼接 | path.join('a', 'b') |
os.path.join('a', 'b') |
| 检查文件存在 | fs.existsSync(path) |
os.path.exists(path) |
两者的差异主要在:Node.js 强调异步非阻塞,所以有大量回调或 Promise 风格的 API;Python 默认是同步的,代码更直观,但处理大文件或高并发时需要额外工具。
十、综合演示:一个简单的笔记管理器
下面这段代码综合运用了本篇学到的文件读写知识,做一个能用 JSON 文件持久化存储的笔记管理器:
import json
import os
from datetime import datetime
NOTES_FILE = 'notes.json'
def load_notes():
"""从 JSON 文件加载笔记列表"""
if not os.path.exists(NOTES_FILE):
return []
with open(NOTES_FILE, 'r', encoding='utf-8') as file:
return json.load(file)
def save_notes(notes):
"""把笔记列表保存到 JSON 文件"""
with open(NOTES_FILE, 'w', encoding='utf-8') as file:
json.dump(notes, file, ensure_ascii=False, indent=2)
def add_note(title, content):
"""添加一条新笔记"""
notes = load_notes()
note = {
'id': len(notes) + 1,
'title': title,
'content': content,
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
notes.append(note)
save_notes(notes)
print(f'笔记"{title}"添加成功')
def list_notes():
"""列出所有笔记"""
notes = load_notes()
if not notes:
print('暂无笔记')
return
print(f'\n===== 我的笔记(共{len(notes)}条)=====')
for note in notes:
print(f'[{note["id"]}] {note["title"]} - {note["created_at"]}')
def view_note(note_id):
"""查看一条笔记的详细内容"""
notes = load_notes()
for note in notes:
if note['id'] == note_id:
print(f'\n===== {note["title"]} =====')
print(f'时间:{note["created_at"]}')
print(f'内容:{note["content"]}')
return
print(f'笔记 {note_id} 不存在')
def delete_note(note_id):
"""删除一条笔记"""
notes = load_notes()
new_notes = [note for note in notes if note['id'] != note_id]
if len(new_notes) == len(notes):
print(f'笔记 {note_id} 不存在')
return
save_notes(new_notes)
print(f'笔记 {note_id} 已删除')
# 测试
if __name__ == '__main__':
add_note('学习 Python', '今天学习了文件读写和 JSON 处理')
add_note('购物清单', '苹果、香蕉、牛奶、面包')
list_notes()
view_note(1)
delete_note(2)
list_notes()
代码设计要点:
load_notes()和save_notes()封装了文件读写的细节,其他函数不需要关心数据是怎么存的。- 如果文件不存在(第一次运行),
load_notes()返回空列表,不会报错。 datetime.now().strftime()生成格式化的时间字符串,记录笔记的创建时间。- 删除操作用了列表推导式过滤掉要删除的笔记,这是 Python 中处理列表的高频写法。
十一、本篇动手练习
练习 1:日志记录器
新建 practice7-1.py,写一个函数 log_message(message),每次调用时把当前时间和消息追加写入 log.txt 文件。格式:[2025-01-15 10:30:00] 用户登录成功。
练习 2:学生成绩管理(JSON 版)
新建 practice7-2.py,用 JSON 文件存储学生成绩数据。实现添加学生成绩、查看所有成绩、按姓名查找成绩。每次操作后自动保存到 JSON 文件。
练习 3:CSV 数据转换
新建 practice7-3.py,读取上一篇练习中的 users.csv,把其中的数据读取出来,转成 JSON 格式写入 users.json。
练习 4:文件搜索工具
新建 practice7-4.py,写一个函数 search_files(directory, keyword),搜索指定目录下所有 .txt 文件,找出包含关键词的行,输出文件名、行号和内容。
十二、本篇小结
这一篇你学会了 Python 中文件读写和数据处理的核心技能:
open()和with语句:with语句自动关闭文件,是读写文件的最佳实践。常用模式:'r'读、'w'覆盖写、'a'追加。- 读文件的四种方式:
read()一次全读、readline()逐行读、readlines()读成列表、for循环直接遍历(最推荐,不占内存)。 - 写文件:
write()写字符串、writelines()写字符串列表。换行符需要手动加\n。 - JSON 处理:
json.dump()写文件、json.load()读文件、json.dumps()转字符串、json.loads()解析字符串。ensure_ascii=False保留中文,indent=2格式化输出。 - CSV 处理:
csv.reader/csv.writer处理列表格式,csv.DictReader/csv.DictWriter处理字典格式。 - 文件和目录操作:
os.path.exists()检查是否存在、os.path.join()拼接路径、os.listdir()列出文件。
文件读写是 Python 日常开发中最常用的技能之一——日志记录、配置管理、数据导出、爬虫存储,处处都能用到。下一篇,我们正式进入 Python 最令人生畏但也最强大的领域——爬虫,用 requests 和 BeautifulSoup 抓取网页数据。
下一篇预告
下一篇——《爬虫入门——用 requests 抓取网页数据》:安装 requests 和 BeautifulSoup、发送 GET 和 POST 请求、解析 HTML 提取数据、处理翻页、设置请求头伪装浏览器。你会写出第一个能自动抓取网页内容的 Python 脚本。
Python 零基础入门,每周更新。













暂无评论内容