Python 文件与输入输出 深度解析
目录
- [Python I/O 概述](#Python I/O 概述)
- 文件对象与基本操作
- 2.1 打开文件:
open与模式 - 2.2 读取数据
- 2.3 写入数据
- 2.4 使用
with自动管理文件
- 2.1 打开文件:
- 文件指针与随机访问
- [路径操作:
os、os.path与pathlib](#路径操作:os、os.path 与 pathlib)- 4.1
os模块与os.path基础 - 4.2 现代路径处理:
pathlib.Path
- 4.1
- 二进制模式与内存视图
- [序列化:
json、pickle与csv](#序列化:json、pickle 与 csv)- 6.1 JSON:通用数据交换格式
- 6.2 Pickle:Python 原生序列化
- 6.3 CSV:逗号分隔值处理
- [内存中的虚拟文件:
StringIO与BytesIO](#内存中的虚拟文件:StringIO 与 BytesIO) - 编码与解码深度解析
- 高级文件处理
- 9.1 目录遍历:
os.walk与pathlib.rglob - 9.2 文件系统操作:拷贝、移动、删除
- 9.3 临时文件与目录:
tempfile
- 9.1 目录遍历:
- 常见陷阱与最佳实践
- 总结
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+' |
python
# 基本用法
file = open('example.txt', 'r', encoding='utf-8')
content = file.read()
file.close()
文本模式下,默认编码为平台相关(通常 UTF-8)。强烈建议显式指定 encoding。
2.2 读取数据
常用的读取方法:
read(size=-1):读取全部或指定字节/字符数。readline():读取一行,含末尾换行符。readlines():读取所有行,返回列表。- 直接迭代文件对象本身:「惰性」逐行读取,内存高效。
python
# 逐行读取
with open('data.txt', 'r') as f:
for line in f:
print(line.strip())
readlines() 会一次性将所有行加载到内存,大文件应避免使用。
2.3 写入数据
write(s):写入字符串或字节。writelines(sequence):写入一个字符串序列,不会自动加换行符。
python
lines = ["第一行\n", "第二行\n", "第三行\n"]
with open('output.txt', 'w') as f:
f.writelines(lines)
2.4 使用 with 自动管理文件
with 语句确保文件在任何情况下(包括异常)都能被正确关闭,无需手动调用 close()。这是推荐做法。
python
with open('example.txt', 'r') as f:
data = f.read()
# 离开 with 块时文件自动关闭
3. 文件指针与随机访问
文件对象维护一个当前位置指针,指示下一次读写的位置。
tell():返回当前指针位置(字节偏移量)。seek(offset, whence=0):移动指针。whence=0:从头开始(默认)。whence=1:从当前位置。whence=2:从文件末尾。
python
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 子模块专门处理路径字符串。
python
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,用面向对象的方式操作路径,代码更直观。
python
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。
python
with open('image.png', 'rb') as src:
data = src.read()
# data 是 bytes 对象
with open('copy.png', 'wb') as dst:
dst.write(data)
处理大型二进制数据时,可以使用 memoryview 实现零拷贝切片。
python
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 字符串相互转换。
python
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 对象序列化为字节流,但仅在可信数据间使用,因为反序列化可能执行恶意代码。
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 文件。
python
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 更方便。
python
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 在内存中模拟文本和二进制流,常用于测试或临时缓冲。
python
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())
python
# 二进制流
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 参数,否则使用平台默认编码,可能产生乱码或错误。
python
# 读取 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)更简洁。
python
import os
for root, dirs, files in os.walk('.'):
for name in files:
print(os.path.join(root, name))
python
from pathlib import Path
for py_file in Path('.').rglob('*.py'):
print(py_file.absolute())
9.2 文件系统操作:拷贝、移动、删除
shutil 模块提供高层次的文件操作。
python
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 用于安全创建临时文件/目录,自动删除。
python
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。
掌握这些技术,你就能高效、安全地进行文件处理,为数据处理、日志系统、配置管理等应用打下坚实基础。