mmap不能直接替换变长字符串,因其映射区域大小固定,无法自动扩容;长度不等时会导致后续数据错位,必须用临时文件重写或仅限等长替换。

为什么直接用 mmap 不能直接替换字符串?
因为 mmap 映射的是字节序列,不是字符串;而且文件长度变化时(比如把 "abc" 替成 "defgh"),底层映射的内存区域大小固定,无法自动扩容。常见错误是调用 replace() 后直接 flush(),结果只改了前 N 字节,后面内容被截断或错位。
真正可行的做法是:先用 mmap 快速定位所有匹配位置(比逐行读快一个数量级),再用原生文件操作做“就地覆盖”或“生成新文件”。
- 匹配阶段用
mmap+find()或正则re.finditer(..., mmap_obj),避免加载全文到内存 - 替换阶段若长度不变(如 UTF-8 编码下英文替换),可直接在
mmap上写入;否则必须另开文件写入 -
mmap对 Windows 的文本换行符(\r\n)敏感,搜索时建议统一用 bytes 模式打开文件
如何用 mmap 高效定位所有匹配项?
核心是避免把整个文件 decode 成 str —— 直接在 mmap 对象上用 bytes 操作或 re 的 bytes 模式扫描。注意 mmap 对象本身支持 find()、rfind(),但不支持 split() 或切片赋值。
- 打开文件必须用
'rb+'模式,否则mmap不可写 - 用
re.finditer(pattern, mmap_obj, re.MULTILINE)时,pattern必须是bytes类型(如b'foo'),且不能用^/$锚点依赖行首/尾逻辑(mmap 不按行解析) - 多次
find()要手动更新起始偏移,例如:pos = mmap_obj.find(b'needle', pos + 1) - 大文件中重复搜索同一 pattern,建议先
mmap_obj.read()一小段(如 1MB)分块处理,防止 long-running mmap 锁住文件
替换时怎么避免覆盖错位或损坏文件?
只有当旧内容和新内容字节长度完全相等时,才能安全地在 mmap 上直接写入。哪怕差 1 字节,都会导致后续所有数据偏移错乱 —— 这是最容易被忽略的硬伤。
立即学习“Python免费学习笔记(深入)”;
- 检查长度:用
len(old_bytes) == len(new_bytes),而不是len(old_str) == len(new_str)(UTF-8 下中文字符占 3 字节) - 写入前必须调用
mmap_obj.flush(),否则修改只存在于内存映射,不落盘 - Windows 下如果文件被其他进程打开(包括资源管理器预览),
mmap可能抛PermissionError,需捕获并 fallback 到普通文件读写 - 若长度不等,老老实实用
tempfile.NamedTemporaryFile写新内容,再原子替换原文件(os.replace())
实际使用中哪些细节会突然报错?
mmap 不是万能加速器,边界条件稍不注意就崩。最常踩的坑不在逻辑,而在系统层限制。
-
OSError: [Errno 22] Invalid argument:通常是文件为空,或mmap大小传了 0 —— 记得先os.stat(path).st_size判断非零 - Linux 下超过 2GB 文件,32 位 Python 会失败;64 位没问题,但 mmap 区域过大可能触发
ENOMEM -
mmap对象不能 pickle,别试图把它传给multiprocessing子进程 —— 每个进程要自己 mmap 同一文件 - 用完必须显式
del mmap_obj或mmap_obj.close(),否则 Windows 下文件一直被占用,删不了
真正省时间的地方,是把“找”这个动作从 O(n) 降到接近 O(n/m)(m 为平均匹配间隔),而不是幻想靠 mmap 把整个替换流程变快。长度不等时,老办法反而更稳。


















