‘r+b’模式支持二进制文件原位修改,文件指针默认位于开头,可精准seek()定位写入而不清空或追加。

直接用 open() 以 "r+b" 模式打开再 seek() 定位写入
Python 修改二进制文件指定字节位置,核心是跳过读取全文件、避免内存膨胀,靠的是随机访问能力。必须用 "r+b" 模式(可读可写、二进制),不能用 "w+b"(会清空原文件)或 "a+b"(seek() 后写入可能不覆盖而是追加,行为不可靠)。
典型错误是先用 "rb" 读出全部内容、改完再全量写回——对大文件(如几百 MB 的固件、视频)极慢且占内存;或者误用 "wb" 导致整个文件被截断。
-
seek(offset, whence=0)中offset是从文件开头算的字节偏移(从 0 开始),whence=0表示绝对位置,最常用 - 写入前务必确认目标位置有足够空间:用
write()写入的字节数必须 ≤ 剩余文件长度,否则会扩展文件(通常不是你想要的) - 单字节修改用
write(b"\xXX");多字节用write(b"\x01\x02\x03")或write(bytes([1,2,3]))
修改第 0x1A 字节(十进制 26)为 0xFF
这是最常见调试场景:比如 patch 固件头校验位、修改 ELF 文件的 e_type 字段、或打游戏存档的金币值。注意十六进制偏移要转成整数传给 seek()。
with open("firmware.bin", "r+b") as f:
f.seek(0x1A) # 定位到第 27 字节(索引 26)
f.write(b"\xFF")
别写成 f.seek("0x1A")(字符串会报错)或 f.seek(1A)(语法错误)。如果偏移来自配置文件或用户输入,记得用 int(offset_str, 0) 自动识别 0x 前缀。
批量修改连续字节段,但不破坏后续数据
想把地址 0x1000 开始的 4 个字节改成 0xDE, 0xAD, 0xBE, 0xEF?直接 write() 即可,只要不超出原文件长度,后面内容完全不动。
- 先
seek(0x1000),再write(b"\xDE\xAD\xBE\xEF")—— 精确覆盖 4 字节 - 如果新内容比原长度短(比如只写 2 字节),后 2 字节保留原值,不会被清零
- 如果新内容更长,且文件末尾不够空间,
write()会写到末尾然后停止(不会报错),导致实际写入少于预期 —— 必须提前用f.seek(0, 2); file_size = f.tell()检查剩余空间
遇到 io.UnsupportedOperation: write 怎么办
这个错误只说明一件事:文件对象没获得写权限。99% 是打开了错误模式:
- 用了
"rb"或"r"—— 改成"r+b" - 用了
"w+b"但忘了原文件已存在 —— 它会先清空,再让你写,但如果你本意是修改而非重建,这就错了 - 文件被其他进程占用(如 Windows 下用资源管理器正打开该文件)—— 关闭占用或换路径测试
- 权限问题(Linux/macOS 下文件无写权限,或挂载为只读)—— 检查
ls -l或os.access(path, os.W_OK)
真正难处理的是“需要原子性更新”:比如改一个关键字节时程序崩溃,导致文件半损坏。这时候不能靠简单 seek/write,得先备份、再修改、最后替换,但这已是另一层健壮性设计了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











