Python 文件与输入输出 深度解析
目录
- 2.1 打开文件:open 与模式
- 2.2 读取数据
- 2.3 写入数据
- 2.4 使用 with 自动管理文件
- 4.1 os 模块与 os.path 基础
- 4.2 现代路径处理:pathlib.Path
- 6.1 JSON:通用数据交换格式
- 6.2 Pickle:Python 原生序列化
- 6.3 CSV:逗号分隔值处理
- 9.1 目录遍历:os.walk 与 pathlib.rglob
- 9.2 文件系统操作:拷贝、移动、删除
- 9.3 临时文件与目录:tempfile
1. Python I/O 概述
输入/输出(I/O)是程序与外部世界交互的基础。Python 提供了一套灵活且强大的 I/O 设施,包括:
- 文件读写(文本与二进制)
- 内存中的流对象(StringIO、BytesIO)
- 多种序列化格式(JSON、Pickle、CSV)
- 目录与文件系统操作(os、pathlib)
- 网络与管道等更高级的 I/O 接口
本章将深入探讨 Python 文件与输入输出相关的核心概念与最佳实践,并配合大量示例。
2. 文件对象与基本操作
2.1 打开文件:open 与模式
内置函数 open(file, mode='r', encoding=None) 返回一个文件对象,mode 决定操作类型:
| 'r' | 读取(默认) |
| 'w' | 写入(覆盖),文件不存在则创建 |
| 'x' | 排它性创建,文件已存在则失败 |
| 'a' | 追加 |
| 'b' | 二进制模式,与以上组合如 'rb' |
| 't' | 文本模式(默认) |
| '+' | 更新(读写),如 'r+' |
# 基本用法
file = open('example.txt', 'r', encoding='utf-8')
content = file.read()
file.close()
文本模式下,默认编码为平台相关(通常 UTF-8)。强烈建议显式指定 encoding。
2.2 读取数据
常用的读取方法:
- read(size=-1):读取全部或指定字节/字符数。
- readline():读取一行,含末尾换行符。
- readlines():读取所有行,返回列表。
- 直接迭代文件对象本身:「惰性」逐行读取,内存高效。
# 逐行读取
with open('data.txt', 'r') as f:
for line in f:
print(line.strip())
readlines() 会一次性将所有行加载到内存,大文件应避免使用。
2.3 写入数据
- write(s):写入字符串或字节。
- writelines(sequence):写入一个字符串序列,不会自动加换行符。
lines = ["第一行\\n", "第二行\\n", "第三行\\n"]
with open('output.txt', 'w') as f:
f.writelines(lines)
2.4 使用 with 自动管理文件
with 语句确保文件在任何情况下(包括异常)都能被正确关闭,无需手动调用 close()。这是推荐做法。
with open('example.txt', 'r') as f:
data = f.read()
# 离开 with 块时文件自动关闭
3. 文件指针与随机访问
文件对象维护一个当前位置指针,指示下一次读写的位置。
- tell():返回当前指针位置(字节偏移量)。
- seek(offset, whence=0):移动指针。
- whence=0:从头开始(默认)。
- whence=1:从当前位置。
- whence=2:从文件末尾。
with open('data.bin', 'rb') as f:
f.seek(0, 2) # 移到文件末尾
size = f.tell() # 获取文件大小
f.seek(0) # 回到开头
first_10 = f.read(10)
文本模式下,seek 的 whence 仅支持 0,且偏移量必须是 tell() 返回的值或 0,因为字符与字节不是一一对应(多字节编码)。
4. 路径操作:os、os.path 与 pathlib
4.1 os 模块与 os.path 基础
os 模块提供许多操作系统功能,os.path 子模块专门处理路径字符串。
import os
# 路径拼接(跨平台)
path = os.path.join('folder', 'sub', 'file.txt')
# 判断绝对路径
os.path.isabs(path)
# 拆分目录与文件名
dir_name, base_name = os.path.split(path)
# 获取扩展名
root, ext = os.path.splitext(base_name)
# 检查是否存在
os.path.exists(path)
这些函数操作的是字符串,不关心路径是否真实存在(除了 exists)。
4.2 现代路径处理:pathlib.Path
Python 3.4+ 引入了 pathlib,用面向对象的方式操作路径,代码更直观。
from pathlib import Path
# 创建路径对象
p = Path('documents') / 'report.txt'
# 常用操作
p.name # 'report.txt'
p.stem # 'report'
p.suffix # '.txt'
p.parent # Path('documents')
p.is_file() # 检查是否为文件
p.is_dir() # 检查是否为目录
p.exists()
# 读写整个文件
content = p.read_text(encoding='utf-8')
p.write_text('Hello, world!')
# 遍历目录
for child in Path('.').iterdir():
print(child)
# 递归匹配
for txt_file in Path('.').rglob('*.txt'):
print(txt_file)
推荐在 Python 3.6+ 的项目中优先使用 pathlib,它更符合 Python 风格。
5. 二进制模式与内存视图
在二进制模式下(如 'rb', 'wb'),文件操作的对象是 bytes 而非 str。
with open('image.png', 'rb') as src:
data = src.read()
# data 是 bytes 对象
with open('copy.png', 'wb') as dst:
dst.write(data)
处理大型二进制数据时,可以使用 memoryview 实现零拷贝切片。
with open('data.bin', 'rb') as f:
buf = memoryview(f.read())
first_half = buf[:len(buf)//2] # 不复制数据
6. 序列化:json、pickle 与 csv
6.1 JSON:通用数据交换格式
json 模块将 Python 对象与 JSON 字符串相互转换。
import json
data = {
'name': 'Alice',
'age': 30,
'skills': ['Python', 'C++']
}
# 序列化为字符串
json_str = json.dumps(data, indent=2, ensure_ascii=False)
print(json_str)
# 反序列化
parsed = json.loads(json_str)
# 直接与文件交互
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2)
with open('data.json', 'r', encoding='utf-8') as f:
loaded = json.load(f)
支持的类型映射:dict → object, list/tuple → array, str → string, int/float → number, True/False → true/false, None → null。自定义对象需要实现编码器。
6.2 Pickle:Python 原生序列化
pickle 可以将几乎任意的 Python 对象序列化为字节流,但仅在可信数据间使用,因为反序列化可能执行恶意代码。
import pickle
obj = {'key': [1, 2, 3], 'set': {4, 5}}
# 序列化
with open('obj.pkl', 'wb') as f:
pickle.dump(obj, f)
# 反序列化
with open('obj.pkl', 'rb') as f:
restored = pickle.load(f)
# 也可以生成字节串
data = pickle.dumps(obj)
pickle 不是跨语言的,且与 Python 版本绑定较紧,长期存储考虑使用 JSON 或其他标准格式。
6.3 CSV:逗号分隔值处理
csv 模块处理 CSV 文件。
import csv
# 读取
with open('data.csv', 'r', newline='', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
print(row)
# 写入
with open('out.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['Name', 'Age'])
writer.writerow(['Alice', 30])
对于字典式操作,使用 csv.DictReader 和 csv.DictWriter 更方便。
with open('data.csv', 'r') as f:
reader = csv.DictReader(f)
for row in reader:
print(row['Name'])
注意:打开 CSV 文件时,推荐用 newline='' 避免平台换行符差异。
7. 内存中的虚拟文件:StringIO 与 BytesIO
io.StringIO 和 io.BytesIO 在内存中模拟文本和二进制流,常用于测试或临时缓冲。
from io import StringIO, BytesIO
# 文本流
sio = StringIO()
sio.write('Hello\\n')
sio.write('World')
print(sio.getvalue()) # 'Hello\\nWorld'
sio.close()
# 像文件一样读取
sio = StringIO('第一行\\n第二行')
for line in sio:
print(line.strip())
# 二进制流
bio = BytesIO()
bio.write(b'\\x00\\x01\\x02')
bio.seek(0)
print(bio.read()) # b'\\x00\\x01\\x02'
8. 编码与解码深度解析
字符编码是将字符映射为字节的方案。Python 内部使用 Unicode,文件 I/O 需要编解码。
- 编码:将字符串转为字节 '你好'.encode('utf-8') → b'\\xe4\\xbd\\xa0\\xe5\\xa5\\xbd'
- 解码:将字节转为字符串 b'\\xe4\\xbd\\xa0\\xe5\\xa5\\xbd'.decode('utf-8') → '你好'
常见编码:
| ASCII | 仅英文字母和符号,单字节 |
| UTF-8 | 可变长度,向后兼容 ASCII,国际通用 |
| GBK | 中文编码 |
| Latin-1 | 西欧语言,单字节 |
打开文件时,务必指定正确的 encoding 参数,否则使用平台默认编码,可能产生乱码或错误。
# 读取 GBK 编码文件
with open('gbk_file.txt', 'r', encoding='gbk') as f:
text = f.read()
# 写入 UTF-8 文件
with open('utf8_file.txt', 'w', encoding='utf-8') as f:
f.write('包含中文字符')
当编解码出错时,可通过 errors 参数控制行为:'strict'(默认,抛异常)、'ignore'(忽略)、'replace'(用 ? 替换)。
9. 高级文件处理
9.1 目录遍历:os.walk 与 pathlib.rglob
- os.walk 递归遍历目录树,返回 (dirpath, dirnames, filenames) 三元组。
- pathlib.rglob(pattern) 更简洁。
import os
for root, dirs, files in os.walk('.'):
for name in files:
print(os.path.join(root, name))
from pathlib import Path
for py_file in Path('.').rglob('*.py'):
print(py_file.absolute())
9.2 文件系统操作:拷贝、移动、删除
shutil 模块提供高层次的文件操作。
import shutil
# 拷贝文件
shutil.copy('src.txt', 'dst.txt')
# 拷贝目录树
shutil.copytree('src_dir', 'dst_dir')
# 移动文件或目录
shutil.move('old', 'new')
# 删除目录树
shutil.rmtree('dir')
基本删除/创建目录可使用 os.remove()、os.mkdir() 等。
9.3 临时文件与目录:tempfile
tempfile 用于安全创建临时文件/目录,自动删除。
import tempfile
# 创建临时文件
with tempfile.NamedTemporaryFile(mode='w+t', suffix='.txt', delete=True) as tmp:
tmp.write('临时内容')
tmp.seek(0)
print(tmp.read())
# 文件自动删除
# 创建临时目录
with tempfile.TemporaryDirectory() as tmpdir:
print(tmpdir)
# 目录自动删除
10. 常见陷阱与最佳实践
- 编码未指定:总是为文本模式指定 encoding='utf-8',避免跨平台问题。
- 大文件一次性读入内存:对大型文件使用逐行迭代或指定块大小读取。
- 忘记关闭文件:使用 with 语句。
- 路径字符串拼接直用 +:使用 os.path.join 或 pathlib.Path / 操作符。
- readlines 遍历大文件:改用 for line in file。
- pickle 加载不安全数据:绝不从不受信任的来源用 pickle.load。
- 文本模式 seek:文本模式下的 seek 受限于字符偏移,尽量在二进制模式进行随机访问。
- CSV 文件中缺少 newline='':会导致在 Windows 平台下出现多余的空白行。
11. 总结
Python 的 I/O 体系涵盖从底层文件操作到高级序列化与路径处理的方方面面。核心要点:
- 使用 with 进行文件管理,规避资源泄露。
- 优先选用 pathlib.Path 进行路径操作,提升可读性。
- 根据数据特点选择序列化格式:通用选 JSON,Python 内部临时用 Pickle,表格数据选 CSV。
- 文本 I/O 必须关注编码;二进制 I/O 则处理 bytes。
掌握这些技术,你就能高效、安全地进行文件处理,为数据处理、日志系统、配置管理等应用打下坚实基础。




