go处理二进制文件需用encoding/binary读写定长结构化数据,io.readfull保底处理字节流;gob仅限go内部序列化;binary.write必须显式传入字节序,结构体字段须导出且类型固定,变长数据需手动拆解长度与内容,读固定长度必须用io.readfull。

Go 处理二进制文件的核心不是“选哪个包”,而是明确:用 encoding/binary 读写结构化定长数据,用 io.ReadFull + []byte 保底处理任意字节流;gob 只适合 Go 内部序列化,跨语言或协议交互必须绕开它。
binary.Write 必须传字节序,否则 panic
binary.Write 第二个参数必须是 binary.LittleEndian 或 binary.BigEndian,漏掉就直接 panic:panic: binary.Write: invalid type interface {}。这不是类型错误,是函数签名校验失败——它把没传的字节序当成了要写的数据。
- 网络协议、Java/C 互操作优先用
binary.BigEndian(即网络字节序) - Windows PE、某些游戏资源、本地调试 dump 常用
binary.LittleEndian - 别依赖“本机默认”:x86_64 Linux 和 macOS 都是小端,但 ARM64 macOS 仍是小端,而网络传输永远要按协议来
结构体字段必须导出且类型固定
binary.Write 和 binary.Read 对结构体只认首字母大写的导出字段,且每个字段类型必须有确定字节长度:不能用 int、string、[]byte、map、指针或 interface{}。
- 用
int32代替int,用[32]byte代替string,用uint16代替int(如果协议明确是 2 字节无符号) - 字段顺序必须和二进制格式定义完全一致,不支持 struct tag(如
`binary:"size=4"`是无效的) - 结构体里有未导出字段(如
private int)会被跳过,不报错也不写入,容易导致后续字段错位
变长数据必须手动拆解长度与内容
encoding/binary 原生不支持字符串、切片等变长类型。想存一个字符串,得自己先写长度前缀,再写字节流;读的时候也得反着来。
- 写法示例:
binary.Write(w, binary.LittleEndian, uint32(len(s))); w.Write([]byte(s)) - 读法示例:
var n uint32; binary.Read(r, binary.LittleEndian, &n); buf := make([]byte, n); io.ReadFull(r, buf) - 长度字段本身必须是定长类型(
uint16最多支持 64KB,uint32支持 4GB),别用int或uint - 读之前务必检查
n是否过大,避免分配超限内存(比如n > 10*1024*1024就直接拒绝)
读固定长度数据必须用 io.ReadFull
直接调 file.Read(buf) 可能只读到部分字节(尤其在文件末尾、网络连接或 buffered reader 场景),导致后续解析全乱。真正安全的做法是用 io.ReadFull 强制读满。
- 错误写法:
n, _ := file.Read(headerBuf)——n可能是 3 而不是 8,但你不检查就继续解析 - 正确写法:
_, err := io.ReadFull(file, headerBuf),err 为io.ErrUnexpectedEOF表示文件提前结束 - 对每个协议头、帧头、加密块,都应预分配精确大小的
[]byte,别用make([]byte, 0, N)然后 append - 如果文件很大,别用
os.ReadFile一次性加载,分块 +io.ReadFull更可控
最常被忽略的一点:binary.Read 不会自动跳过结构体填充字节,也不会校验 magic number 或 CRC。协议越简单,越要靠人工加字段对齐、长度校验、边界检查来兜底——没有银弹,只有字节对得上才算数。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











