Python 的 I/O(输入/输出)系统是处理数据流的核心机制,涵盖文件操作、内存流、标准输入输出、序列化等。下面将从基础到高级进行全面讲解,包含详细原理和示例。
一、文件 I/O 深度解析
1. 文件打开模式详解
模式 | 描述 | 文件存在 | 文件不存在 | 指针位置 |
---|---|---|---|---|
'r' |
只读 | 打开 | 报错(FileNotFoundError) | 开头 |
'r+' |
读写 | 打开 | 报错 | 开头 |
'w' |
只写 | 清空 | 创建 | 开头 |
'w+' |
读写 | 清空 | 创建 | 开头 |
'a' |
追加 | 打开 | 创建 | 末尾 |
'a+' |
读写追加 | 打开 | 创建 | 末尾 |
'x' |
排他创建 | 报错(FileExistsError) | 创建 | 开头 |
'b' |
二进制模式(需组合使用) | |||
't' |
文本模式(默认) |
重要特性:
- 二进制模式(
b
)与文本模式(t
)互斥 +
号启用读写功能,但行为因主模式而异- Windows 系统中换行符自动转换(文本模式)
2. 文件操作全方法
python
with open('data.txt', 'r+', encoding='utf-8') as f:
# 读取操作
print(f.read(10)) # 读取前10个字符
print(f.readline()) # 读取下一行
print(f.readlines()) # 剩余所有行 -> 列表
# 写入操作
f.write("新内容\n") # 在当前位置写入
f.writelines(["行1\n", "行2\n"])
# 指针控制
f.seek(5) # 移动到第5字节
print(f.tell()) # 输出当前位置 -> 5
f.seek(0, 2) # 移动到文件末尾
# 缓冲区控制
f.flush() # 强制写入磁盘
# 文件截断
f.truncate(20) # 截断文件到20字节
3. 二进制文件操作
python
# 图像复制
with open('source.jpg', 'rb') as src, open('copy.jpg', 'wb') as dst:
while True:
chunk = src.read(4096) # 分块读取(4KB)
if not chunk:
break
dst.write(chunk)
二、上下文管理器原理
with
语句实现原理:
python
class ManagedFile:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
# 可处理异常
if exc_type:
print(f"异常发生: {exc_val}")
return True # 抑制异常
# 使用自定义上下文管理器
with ManagedFile('data.txt', 'w') as f:
f.write("上下文管理")
三、标准 I/O 流详解
python
import sys
# 重定向标准输出
with open('output.log', 'w') as f:
sys.stdout = f
print("此内容写入文件")
sys.stdout = sys.__stdout__ # 恢复
# 非阻塞输入检查
import select
while True:
# 检查标准输入是否有数据
rlist, _, _ = select.select([sys.stdin], [], [], 0.1)
if rlist:
line = sys.stdin.readline().strip()
if line == 'exit':
break
print(f"收到: {line}")
else:
print("等待输入...")
四、内存流高级应用
1. StringIO 复杂操作
python
from io import StringIO, BytesIO
# 文本流
stream = StringIO()
stream.write("初始内容")
stream.seek(0)
# 插入内容到指定位置
content = stream.getvalue()
new_content = content[:3] + "插入" + content[3:]
stream = StringIO(new_content)
# 流转换
binary_stream = BytesIO(stream.getvalue().encode('utf-8'))
2. BytesIO 二进制处理
python
# 创建PNG文件头
png_header = bytes([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])
img_data = BytesIO()
img_data.write(png_header)
img_data.write(b"\x00\x00\x00\x0D") # IHDR长度
img_data.write(b"IHDR") # 块类型
# 验证文件头
img_data.seek(0)
if img_data.read(8) == png_header:
print("有效PNG文件")
五、序列化高级技巧
1. Pickle 安全与优化
python
import pickle
import functools
class ComplexObject:
def __init__(self, data):
self.data = data
# 自定义序列化
def __getstate__(self):
return {"safe_data": self.data * 2}
# 自定义反序列化
def __setstate__(self, state):
self.data = state["safe_data"] / 2
# 高效序列化协议
obj = ComplexObject(42)
with open('data.pkl', 'wb') as f:
# 使用协议版本5(Python 3.8+)
pickle.dump(obj, f, protocol=5, buffer_callback=functools.partial(f.write))
# 增量加载
unpickler = pickle.Unpickler(open('data.pkl', 'rb'))
while True:
try:
item = unpickler.load()
except EOFError:
break
2. JSON 高级处理
python
import json
from datetime import datetime
import numpy as np
class CustomEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, np.ndarray):
return obj.tolist()
return super().default(obj)
data = {
"time": datetime.now(),
"matrix": np.array([[1, 2], [3, 4]])
}
# 自定义序列化
json_str = json.dumps(data, cls=CustomEncoder, indent=2)
# 自定义反序列化
def object_hook(dct):
if 'time' in dct:
dct['time'] = datetime.fromisoformat(dct['time'])
return dct
data = json.loads(json_str, object_hook=object_hook)
六、高级 I/O 控制
1. 缓冲机制深度
缓冲类型 | 设置方式 | 刷新条件 | 适用场景 |
---|---|---|---|
无缓冲 | buffering=0 |
立即写入 | 实时日志 |
行缓冲 | buffering=1 |
遇到换行符 | 终端交互 |
全缓冲 | buffering>1 |
缓冲区满 | 文件操作 |
python
# 行缓冲实时监控
with open('log.txt', 'w', buffering=1) as f: # 行缓冲
for i in range(5):
f.write(f"日志条目 {i}\n")
input("按回车继续") # 每次写入后立即刷新
2. 内存映射文件
python
import mmap
with open('large.bin', 'r+b') as f:
# 创建内存映射
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE)
# 随机访问
print(mm[1000:1020]) # 读取字节
# 修改内容
mm[500:510] = b'X' * 10
# 搜索内容
pos = mm.find(b'PATTERN')
if pos != -1:
mm.seek(pos)
mm.write(b'REPLACEMENT')
mm.close() # 必须手动关闭
七、文件系统高级操作
1. 目录遍历优化
python
import os
from pathlib import Path
# 高效遍历大目录
def scan_large_dir(path):
with os.scandir(path) as entries:
for entry in entries:
if entry.is_dir():
print(f"目录: {entry.name}")
elif entry.is_file():
print(f"文件: {entry.name} ({entry.stat().st_size}字节)")
# 使用pathlib递归处理
root = Path('project')
for file in root.glob('**/*.py'): # 所有Python文件
print(f"Python文件: {file.relative_to(root)}")
2. 文件锁机制
python
import fcntl
def safe_write(path, content):
with open(path, 'a') as f:
# 获取排他锁
fcntl.flock(f, fcntl.LOCK_EX)
try:
f.write(content + "\n")
finally:
# 释放锁
fcntl.flock(f, fcntl.LOCK_UN)
# 多进程安全写入
from multiprocessing import Pool
with Pool(4) as p:
p.map(lambda x: safe_write('data.txt', f"进程{x}"), range(10))
八、异步 I/O (asyncio)
python
import asyncio
import aiofiles
async def async_file_ops():
# 异步写入
async with aiofiles.open('async.txt', 'w') as f:
await f.write("异步写入内容\n")
await f.flush()
# 异步读取
async with aiofiles.open('async.txt', 'r') as f:
content = await f.read()
print(f"读取内容: {content}")
# 运行异步任务
asyncio.run(async_file_ops())
# 高性能日志记录器
class AsyncLogger:
def __init__(self, filename):
self.filename = filename
self.queue = asyncio.Queue()
self.task = asyncio.create_task(self._writer())
async def log(self, message):
await self.queue.put(f"{asyncio.get_event_loop().time()}: {message}\n")
async def _writer(self):
async with aiofiles.open(self.filename, 'a') as f:
while True:
message = await self.queue.get()
await f.write(message)
await f.flush()
九、性能优化策略
- 大文件处理黄金法则
python
# 分块读取处理
CHUNK_SIZE = 16 * 1024 # 16KB
with open('huge.log', 'r') as f:
while True:
chunk = f.read(CHUNK_SIZE)
if not chunk:
break
process(chunk)
# 零拷贝技术 (Linux)
import os
def zero_copy(source, dest):
os.sendfile(dest.fileno(), source.fileno(), None, os.path.getsize(source))
- 缓冲区优化
python
# 自定义缓冲区
class BufferedWriter:
def __init__(self, file, buffer_size=8192):
self.file = file
self.buffer = bytearray(buffer_size)
self.pos = 0
def write(self, data):
data = memoryview(data)
while data:
n = min(len(data), len(self.buffer) - self.pos)
self.buffer[self.pos:self.pos+n] = data[:n]
self.pos += n
data = data[n:]
if self.pos == len(self.buffer):
self.flush()
def flush(self):
if self.pos > 0:
self.file.write(self.buffer[:self.pos])
self.pos = 0
def close(self):
self.flush()
self.file.close()
# 使用自定义缓冲
with open('optimized.bin', 'wb') as raw_f:
with BufferedWriter(raw_f, 65536) as buf_f: # 64KB缓冲
for _ in range(10000):
buf_f.write(b'x' * 1024) # 1KB写入
最佳实践总结
-
资源管理
- 始终使用
with
语句确保资源释放 - 长时间打开的文件定期
flush()
- 使用
try/finally
作为with
的备选
- 始终使用
-
编码处理
- 显式指定编码:
encoding='utf-8'
- 处理编码错误:
errors='replace'
或errors='ignore'
- 二进制数据坚持使用
b
模式
- 显式指定编码:
-
性能关键点
- 大文件使用迭代处理:
for line in file
- 避免频繁小量写入,使用缓冲
- 减少系统调用次数(批量操作)
- 大文件使用迭代处理:
-
高级技巧
- 内存映射处理超大文件
- 异步 I/O 处理高并发
- 文件锁保证多进程安全
-
调试技巧
- 使用
file.tell()
追踪指针位置 - 检查
os.stat()
获取文件状态 - 监控
io
模块的DEFAULT_BUFFER_SIZE
- 使用
掌握这些 I/O 技术,可高效处理从简单文本到 TB 级数据集的各类场景。