binary.read 必须显式指定字节序,否则因字节序不匹配导致字段错乱或 panic;结构体需用固定大小类型且无填充;binary.write 不支持 Struct{} 和 [0]byte;int 类型不可用于 binary 包,须用 int32 等明确位宽类型。

binary.Read 读取结构体时字节序不匹配就 panic
go 的 binary.Read 不会自动推断字节序,必须显式传入 binary.LittleEndian 或 binary.BigEndian。如果文件是小端写的,你却用大端读,binary.Read 会把前 4 字节当做一个错误的 uint32 值(比如 0x01000000 变成 16777216),后续字段全错,甚至直接触发 io.ErrUnexpectedEOF 或整数溢出 panic。
- 永远先确认源文件的字节序——查文档、看协议规范、或用
xxd -c 1 file.bin手动比对已知值 - 结构体字段必须是固定大小的类型:
uint32、int64、[32]byte,不能含String、slice、map - 结构体不能有填充字节(padding);如有需要,用
_ [N]byte显式占位,否则内存布局和文件布局对不上 - 示例:读一个 8 字节头(magic uint32 + version uint32),小端存储:
type Header struct { Magic uint32 Version uint32 } var h Header err := binary.Read(r, binary.LittleEndian, &h)
写二进制时 struct{} 和 [0]byte 会导致 binary.Write 失败
binary.Write 要求目标类型可序列化为确定长度的字节流。struct{} 长度为 0,[0]byte 也是 0 字节,但 binary.Write 内部会调用 reflect.Value.Size(),对零长类型返回 0 后直接报 binary.ErrInvalid。
- 避免在待序列化的结构体中嵌入空结构体或零长数组
- 如果只是占位或标记,改用
byte(值设为 0)或明确长度的数组,如[1]byte - 写入前可用
unsafe.Sizeof(v)快速验证结构体是否“有内容”:返回 0 就别往binary.Write里塞
跨平台读写要注意 int 类型默认不参与 binary 包编解码
Go 的 int 和 uint 长度依赖平台(32 位 vs 64 位),binary.Read 和 binary.Write 根本不支持它们——调用时会直接 panic 报 binary: invalid type int。
- 必须用明确位宽的类型:
int32、uint64、int16等 - 尤其注意从 C 互操作或网络协议迁移来的代码:C 的
int常是 32 位,但 Go 的int在 macos ARM64 是 64 位,混用必崩 - 如果协议文档写的是 “4-byte signed Integer”,就无条件用
int32,别想当然替换成int
binary.Write 性能差?别在循环里反复调用
binary.Write 每次都做反射检查 + 类型判断 + 字节序转换,开销远高于直接写 []byte。在高频写场景(如日志批量落盘、序列化消息体),反复调用它会成为瓶颈。
立即学习“go语言免费学习笔记(深入)”;
- 批量写入时,优先拼接好完整
[]byte,再一次性io.Write;或用bytes.Buffer+binary.Write到 buffer,最后buffer.Bytes() - 若结构体字段少且固定,手写
WriteXXX方法更可控:比如func (h *Header) WriteTo(w io.Writer) Error { binary.Write(w, binary.LittleEndian, h.Magic); return binary.Write(w, binary.LittleEndian, h.Version) } - 注意:buffer 写入后记得
buffer.Reset(),否则下次写会追加而非覆盖
大小端不是选哪个“更好”,而是必须和文件/协议定义严格一致;最容易被忽略的,是结构体里隐藏的 padding 字节和 int 类型的平台陷阱——它们不会报错,但会让你读出来的数据看起来“偶尔错一点”。