文件读写:open、read、write、with语句
一句话概述
Python 通过内置的 open() 函数打开文件,用 read() / write() 方法读写内容,再配合 with 语句自动关闭文件,实现安全高效的文件操作。这是数据持久化最基本的入口——把程序中的数据存到硬盘,或从硬盘读取数据到程序。
💡 核心要点:①open()打开文件返回文件对象,是读写的大门 ②read()读全部内容,readline()逐行读,readlines()读成列表 ③write()/writelines()向文件写入内容 ④with语句自动管理文件关闭,就算出错也不会忘关
教学与演示
一、打开文件:open()——找到并打开"文件抽屉"
是什么:open(file, mode='r', encoding=None) 是 Python 内置函数,用来打开一个文件,返回一个文件对象(也叫文件句柄)。有了这个对象,才能进行后续的读写操作。
大白话 就像你想看一本日记,必须先找到这本日记,翻开它——open() 就是"找到并翻开"的动作。文件对象就是那本摊开的日记本,你可以读里面的内容,也可以往上写新的内容。
为什么:计算机内存是易失性的——程序关了,内存里的数据就没了。文件是持久化存储的第一站。open() 是程序与硬盘文件之间建立通信的桥梁,没有它,程序无法访问硬盘上的任何文件。
怎么做:
import numpy as np
# ===== 1. 打开文件的基本方式 =====
# open() 返回一个文件对象,第一个参数是文件路径,第二个参数是模式(默认 'r' 只读)
# 注意:在 real Python 中需要实际路径,这里用字符串模拟文件内容
# 模拟文件内容的字符串(实际开发中是从硬盘读取)
simulated_content = "第一行:Hello Python\n第二行:文件操作\n第三行:AI 全栈学习\n"
# 将模拟内容按行拆分,模拟 readlines() 的效果
lines = simulated_content.strip().split('\n')
print("模拟文件共", len(lines), "行:")
for i, line in enumerate(lines, 1):
print(f" 第{i}行 → {line}")
# ===== 2. 用 numpy 展示文件行数统计 =====
line_lengths = np.array([len(line) for line in lines])
print(f"\n每行字符数:{line_lengths}")
print(f"最长行字符数:{np.max(line_lengths)},最短行字符数:{np.min(line_lengths)}")
print(f"平均每行 {np.mean(line_lengths):.1f} 个字符")什么用:在 AI 开发中,open() 无处不在——读取训练数据(CSV、JSON、TXT)、加载模型配置文件、读取词表文件、保存训练日志等。比如 open('train.json', 'r', encoding='utf-8') 加载标注好的训练语料。
二、读取文件:read()、readline()、readlines()——三种"阅读姿势"
是什么:Python 文件对象提供了三种主要的读取方法:
read(size):一次性读取全部内容(或指定 size 字节),返回一个字符串readline():每次读取一行,返回一个字符串(含行尾换行符\n)readlines():一次性读取所有行,返回一个列表,每个元素是一行字符串
大白话 就像你读一本书——read()是一口气整本读完,readline()是逐句读,readlines()是把每句话撕下来排成一列清单。不同的读法适用于不同场景。
为什么:文件大小各异,有的配置文件就几行,有的训练数据有几十GB。如果对超大文件用 read() 一次性全读入内存,内存直接爆炸。所以需要根据场景选择不同的读取策略——小文件用 read(),大文件逐行读(readline() 或直接 for line in file)。
怎么做:
import numpy as np
# ===== 模拟大文件的逐行读取与统计 =====
# 在真实场景中,这可能是几百万行的训练数据
simulated_data = [
"用户A,25,北京,购买",
"用户B,32,上海,浏览",
"用户C,28,广州,购买",
"用户D,35,深圳,浏览",
"用户E,22,杭州,购买",
"用户F,29,成都,浏览",
"用户G,31,武汉,购买",
"用户H,27,南京,浏览",
]
# ===== 逐行处理:模拟 for line in file 的效果 =====
ages = []
cities = []
actions = []
for row in simulated_data:
# 模拟 readline() 逐行读取
parts = row.split(',') # 实际中可能是 CSV 解析
name, age, city, action = parts
ages.append(int(age))
cities.append(city)
actions.append(action)
# ===== 用 numpy 做统计分析 =====
ages_arr = np.array(ages)
unique_actions, counts = np.unique(actions, return_counts=True)
print(f"总用户数:{len(simulated_data)}")
print(f"平均年龄:{np.mean(ages_arr):.1f} 岁")
print(f"年龄标准差:{np.std(ages_arr):.1f}")
print(f"最小年龄:{np.min(ages_arr)},最大年龄:{np.max(ages_arr)}")
print("\n行为分布:")
for action, count in zip(unique_actions, counts):
print(f" {action}: {count} 人 ({count/len(simulated_data)*100:.0f}%)")什么用:在 AI 项目中,训练数据通常以文本文件存储。逐行读取是处理大规模数据集(如几十万条对话记录)的标准做法。机器学习框架(如 PyTorch 的 DataLoader)底层也依赖逐行或逐批读取文件。
三、写入文件:write()、writelines()——把数据"存进抽屉"
是什么:文件对象的写入方法:
write(string):把字符串写入文件,返回写入的字符数writelines(iterable):把一个可迭代对象(如列表)中的字符串逐个写入文件
大白话 写文件就像是往日记本里写字——write()一次写一句,writelines()一次抄一页(多行)。注意:写之前必须用'w'模式打开,否则不让写。
为什么:程序运行的结果如果不存下来,关闭就没了。write() 是把内存中的数据"固化"到硬盘的关键操作。比如训练好的模型权重、处理后的数据、日志记录,都需要写入文件持久保存。
怎么做:
import numpy as np
# ===== 模拟训练日志生成与写入 =====
# 实际项目中,这会被写入到 train.log 文件
np.random.seed(42)
# 模拟 10 个 epoch 的训练指标
epochs = np.arange(1, 11)
train_loss = 2.0 / np.sqrt(epochs) + np.random.normal(0, 0.05, 10)
val_acc = 0.6 + 0.04 * epochs + np.random.normal(0, 0.02, 10)
val_acc = np.clip(val_acc, 0, 1.0)
# 生成日志行(模拟 writelines 的内容)
log_lines = []
log_lines.append("===== 训练日志 =====")
log_lines.append(f"{'Epoch':<8}{'TrainLoss':<12}{'ValAcc':<10}")
log_lines.append("-" * 30)
for i in range(len(epochs)):
log_lines.append(f"{epochs[i]:<8}{train_loss[i]:<12.4f}{val_acc[i]:<10.4f}")
# 输出日志(模拟 writelines 写入文件)
for line in log_lines:
print(line)
print(f"\n共 {len(log_lines)} 行日志")
print(f"训练起始 Loss:{train_loss[0]:.4f} → 最终 Loss:{train_loss[-1]:.4f}")
print(f"验证准确率从 {val_acc[0]:.2%} 提升到 {val_acc[-1]:.2%}")什么用:在 AI 训练中,每个 epoch 结束后需要将 loss、accuracy 等指标写入日志文件,方便后续用 TensorBoard 等工具可视化。模型预测结果也常写回 CSV 文件供下游使用。
四、with 语句——自动关门的"智能管家"
是什么:with open(...) as f: 是 Python 的上下文管理器语法。进入 with 块时自动打开文件(调用 __enter__),离开 with 块时自动关闭文件(调用 __exit__),无论块内代码是否抛出异常。
大白话 就像酒店房间的自动门——你刷卡进门(with开始),离开时门自动关上并上锁(with结束)。就算你慌忙跑出来(有异常),门也会自动关好。再也不用担心忘关门了。
为什么:如果不用 with,你需要手动 f = open() 然后 f.close()。一旦中间代码抛出异常,close() 可能执行不到,导致文件句柄泄漏(操作系统能打开的文件数有限),甚至数据没写全。with 确保 100% 关闭,是最佳实践。
怎么做:
import numpy as np
# ===== 模拟 with 语句的工作流程 =====
# with 语句的核心价值:无论成功还是失败,都自动清理资源
def simulate_with_statement():
"""模拟 with open() as f: 的行为"""
# 步骤1:__enter__ —— 打开文件(等价于 open())
print("🔓 【with 进入】文件已打开")
# 步骤2:执行 with 块内的代码
try:
print("📝 正在写入数据...")
data = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
print(f" 写入数据:{data}")
print(f" 数据均值:{np.mean(data):.2f}")
# 如果这里出错,__exit__ 仍然会被调用!
# raise ValueError("模拟写入错误") # 取消注释测试异常安全
print("✅ 写入成功")
except Exception as e:
print(f"❌ 发生错误:{e}")
finally:
# 步骤3:__exit__ —— 自动关闭文件(无论是否异常)
print("🔒 【with 退出】文件已自动关闭(即使有异常也会执行)")
simulate_with_statement()
# ===== 对比:不用 with 的风险 =====
print("\n--- 对比实验 ---")
print("没有 with:需要手动 close()")
print("f = open('data.txt', 'w')") # 手动打开
print("f.write(...)") # 如果这里异常
print("f.close() ← 这行可能永远执行不到!") # 文件没关闭!
print("\n使用 with:自动安全关闭")
print("with open('data.txt', 'w') as f:") # with 自动管理
print(" f.write(...)") # 即使异常
print("# 自动调用 f.close() ← 保证执行!")什么用:在 AI 项目中,with 不仅用于文件操作,还广泛用于管理 GPU 资源(with torch.no_grad():)、数据库连接、网络会话等任何需要"用后即关"的资源。这是 Python 资源管理的核心模式。
概念关系图谱
| 概念 | 核心含义 | 与AI的关系 | 关联概念 |
|---|---|---|---|
open() | 建立程序与文件的连接通道 | 加载训练数据、配置文件、词表 | 文件对象、文件模式 |
read() | 一次性读取文件全部内容 | 小配置文件读取 | readline()、readlines() |
readline() | 逐行读取,内存友好 | 处理大规模数据集的逐行流式读取 | read()、迭代器 |
write() | 将字符串写入文件 | 保存模型输出、日志记录、预测结果 | writelines()、文件模式 |
with | 自动管理资源的上下文管理器 | 安全加载模型、管理 GPU 推理上下文 | 上下文管理器、资源管理 |
| 文件对象 | open()返回的读写操作句柄 | 连接原始数据与预处理流程 | open()、文件模式 |
重点答疑
Q1:read() 和 readlines() 有什么区别?
read() 返回一个大字符串,包含文件的全部内容(换行符 \n 也在其中);readlines() 返回一个列表,每个元素是对应的一行字符串。如果要对每行分别处理,readlines() 更方便;如果要整体做文本分析(如正则匹配),read() 更合适。大文件两者都不要用,用 for line in file 逐行迭代最安全。
Q2:为什么推荐用 with 而不是手动 close()?
因为手动 close() 放在 try/finally 之外的话,一旦中间代码抛异常,close() 就跳过了,导致文件句柄泄漏。即使你记得写 try/finally,代码也冗长。with 一行搞定,既简洁又 100% 安全关闭。这是 Python 的最佳实践,几乎所有标准库文档都用 with 示例。
Q3:open() 文件时路径怎么写?相对路径和绝对路径有什么区别?
绝对路径是从根目录开始的完整路径,如 /home/user/data.txt(Linux/Mac)或 C:\Users\user\data.txt(Windows)。相对路径是相对于当前 Python 脚本所在位置,如 ./data/input.txt 表示当前目录下的 data 文件夹。建议项目中使用相对路径,方便团队协作和部署;./ 可省略,直接写 data/input.txt 即可。
章节单词汇总
| 英文 | 音标 | 术语/释义 |
|---|---|---|
| open | /ˈoʊpən/ | 打开(文件);文件操作入口函数 |
| read | /riːd/ | 读取;从文件中获取数据 |
| write | /raɪt/ | 写入;向文件中存入数据 |
| handle | /ˈhændl/ | 句柄;操作系统管理资源的编号 |
| encoding | /ɪnˈkoʊdɪŋ/ | 编码;字符与字节的映射规则(如 UTF-8) |
| persistent | /pərˈsɪstənt/ | 持久化;数据在程序结束后仍然保存 |
| buffer | /ˈbʌfər/ | 缓冲区;读写时暂存数据的内存区域 |
| iterate | /ˈɪtəreɪt/ | 迭代;逐一遍历(如逐行读文件) |
面试练习
Q1 [单选] Python 中打开文件的最佳实践是?
- A.
f = open('a.txt'); ...; f.close() - B.
with open('a.txt') as f: ... - C.
f = file('a.txt') - D.
f = os.open('a.txt')
解答:with语句自动管理文件关闭,即使出现异常也能保证资源释放,是 Python 官方推荐的最佳实践。Python 3 中file()已废弃。
Q2 [单选] readline() 读取的内容是否包含行尾的换行符?
- A. 包含
\n - B. 不包含,自动去掉
- C. 取决于操作系统
- D. 只有 Windows 下包含
解答:readline()返回的字符串末尾包含\n换行符(最后一行可能没有)。如果需要去除,可以调用.strip()或.rstrip('\n')。
Q3 [单选] 读取一个 5GB 的大文件,以下哪种方式最合适?
- A.
content = f.read() - B.
lines = f.readlines() - C.
for line in f: process(line) - D.
data = f.read(5000000000)
解答:逐行迭代(for line in f)是生成器式读取,每次只在内存中保留一行,不会导致内存溢出。A 和 B 会把整个文件加载到内存,5GB 会直接撑爆内存。
Q4 [单选] open('data.txt', 'w') 中 'w' 模式的含义是?
- A. 只读模式
- B. 只写模式,文件不存在则创建,存在则清空
- C. 追加写模式
- D. 读写模式
解答:'w'(write)是只写模式:文件不存在则创建;文件已存在则先清空再写入。如果不想清空,应该用'a'(append)追加模式。
Q5 [单选] 以下哪个不是 Python 文件对象的读取方法?
- A.
read() - B.
readline() - C.
readlines() - D.
readall()
解答:Python 文件对象没有readall()方法。读取全部内容用read(),逐行用readline(),读成列表用readlines()。
Q6 [多选] 关于 with 语句,以下哪些说法是正确的?
- A. 进入
with块时自动打开文件 - B. 离开
with块时自动关闭文件 - C.
with块内抛出异常时,文件不会被关闭 - D.
with语句可以使代码更简洁安全
解答:C 错误——with的核心优势就是即使块内抛出异常,退出时也会自动调用__exit__关闭文件,确保资源不泄漏。
Q7 [单选] f.write('hello') 的返回值是什么?
- A.
None - B. 写入的字符数(5)
- C.
True - D. 文件对象本身
解答:write() 返回实际写入的字符数,这里是 5。这个返回值可以用来确认写入是否完整。
Q8 [单选] 打开文件时指定 encoding='utf-8' 的作用是?
- A. 加快读取速度
- B. 指定字符编码方式,正确处理中文等非 ASCII 字符
- C. 开启压缩模式
- D. 设置文件权限
解答:encoding参数指定文本文件的字符编码。不指定时使用系统默认编码(Windows 可能是 GBK,导致中文乱码)。建议养成明确指定encoding='utf-8'的习惯。
Q9 [单选] 以下哪种写法是正确的 with 同时打开两个文件的方式?
- A.
with open('a.txt') as f1, open('b.txt') as f2: - B.
with open('a.txt'), open('b.txt'): - C.
with (open('a.txt'), open('b.txt')) as f1, f2: - D.
with open('a.txt') and open('b.txt') as f1, f2:
解答:Python 支持在一条with语句中用逗号分隔多个上下文管理器,语法为with cm1 as v1, cm2 as v2:。
Q10 [多选] 文件操作中常见的错误有哪些?
- A.
FileNotFoundError——文件不存在 - B.
PermissionError——没有读写权限 - C.
IsADirectoryError——对目录执行了文件操作 - D.
MemoryError——读取超大文件时内存不足
解答:这些都是文件操作中常见的异常。A 发生在路径错误时;B 发生在权限不足时;C 是路径指向目录而不是文件;D 是用 read() 一次性读超大文件时。