encoding/binary - 二进制编解码
概述
encoding/binary 包提供了二进制和 Go 值之间的互转功能。
encoding/binary 是什么:
- 📦 二进制序列化:将 Go 基本类型编码为字节序列
- 🔧 字节序控制:支持大端(BigEndian)和小端(LittleEndian)
- 📋 定长数据:处理固定长度的二进制数据
- 🛠️ 底层操作:直接操作内存布局和字节序
主要用途:
- 🌐 网络协议:实现自定义网络协议
- 📧 文件格式:解析和生成二进制文件格式
- 🔐 加密解密:处理加密算法的字节数据
- 📊 数据库:存储和读取二进制数据
- 🖼️ 多媒体:解析图片、音频、视频格式
- 🔑 系统编程:与硬件和系统接口交互
重要说明:
- ⚠️ 仅支持基本类型:只支持整数、浮点数等基本类型
- ⚠️ 不支持切片和映射:不能直接编码复杂数据结构
- ⚠️ 字节序敏感:必须明确指定字节序(大端或小端)
- ⚠️ 内存对齐:注意结构体的内存对齐问题
- ✅ 高性能:直接的内存操作,性能优异
- ✅ 零拷贝:某些操作可以直接使用底层内存
- ✅ 标准库支持:Go 标准库提供完整支持
与其他编码包的比较:
| 包 | 编码格式 | 可读性 | 大小 | 用途 |
|---|---|---|---|---|
| encoding/binary | 二进制 | 不可读 | 最小 | 底层数据、协议 |
| encoding/json | JSON 文本 | 可读 | 较大 | Web API、配置 |
| encoding/gob | Gob 二进制 | 不可读 | 中等 | Go 程序间通信 |
| encoding/xml | XML 文本 | 可读 | 最大 | Web 服务、配置 |
字节序(Byte Order)
什么是字节序
字节序是指多字节数据在内存中的存储顺序。
两种字节序:
16 位整数:0x1234
大端序(Big Endian):
高位字节在前:[0x12, 0x34]
内存地址:低 → 高
人类阅读习惯
小端序(Little Endian):
低位字节在前:[0x34, 0x12]
内存地址:低 → 高
x86/x64 架构使用
32 位整数示例(0x12345678):
大端序:[0x12, 0x34, 0x56, 0x78]
小端序:[0x78, 0x56, 0x34, 0x12]
字节序的选择
大端序(Big Endian):
- ✅ 网络字节序(Network Byte Order)
- ✅ 人类阅读习惯(从左到右)
- ✅ 协议标准(TCP/IP、HTTP)
- 📌 使用:
binary.BigEndian
小端序(Little Endian):
- ✅ x86/x64 架构原生
- ✅ 性能略优(无需转换)
- ✅ Windows、Linux 默认
- 📌 使用:
binary.LittleEndian
判断当前系统字节序:
func isLittleEndian() bool {
var i int16 = 0x0102
b := (*[2]byte)(unsafe.Pointer(&i))
return b[0] == 0x02 // true = 小端,false = 大端
}
核心类型
1. ByteOrder - 字节序接口
type ByteOrder interface {
Uint16([]byte) uint16
Uint32([]byte) uint32
Uint64([]byte) uint64
PutUint16([]byte, uint16)
PutUint32([]byte, uint32)
PutUint64([]byte, uint64)
String() string
}
功能:定义字节序操作的接口。
预定义实现:
var (
BigEndian ByteOrder // 大端序
LittleEndian ByteOrder // 小端序
)
主要方法:
// 读取(从字节到整数)
func (ByteOrder) Uint16(b []byte) uint16
func (ByteOrder) Uint32(b []byte) uint32
func (ByteOrder) Uint64(b []byte) uint64
func (ByteOrder) Uint16(b []byte) uint16
func (ByteOrder) Int16(b []byte) int16
func (ByteOrder) Int32(b []byte) int32
func (ByteOrder) Int64(b []byte) int64
func (ByteOrder) Float32(b []byte) float32
func (ByteOrder) Float64(b []byte) float64
// 写入(从整数到字节)
func (ByteOrder) PutUint16(b []byte, v uint16)
func (ByteOrder) PutUint32(b []byte, v uint32)
func (ByteOrder) PutUint64(b []byte, v uint64)
func (ByteOrder) PutInt16(b []byte, v int16)
func (ByteOrder) PutInt32(b []byte, v int32)
func (ByteOrder) PutInt64(b []byte, v int64)
func (ByteOrder) PutFloat32(b []byte, v float32)
func (ByteOrder) PutFloat64(b []byte, v float64)
2. Encoder - 编码器
type Encoder struct {
// 包含过滤或未导出的字段
}
功能:将 Go 值编码为二进制数据。
创建方法:
func NewEncoder(w io.Writer) *Encoder
主要方法:
// 设置字节序
func (enc *Encoder) Order() ByteOrder
// 编码单个值
func (enc *Encoder) Encode(v interface{}) error
// 编码多个值
func (enc *Encoder) Encode(v ...interface{}) error
使用示例:
var buf bytes.Buffer
enc := binary.NewEncoder(&buf)
enc.Order(binary.BigEndian)
enc.Encode(uint32(12345))
3. Decoder - 解码器
type Decoder struct {
// 包含过滤或未导出的字段
}
功能:从二进制数据解码为 Go 值。
创建方法:
func NewDecoder(r io.Reader) *Decoder
主要方法:
// 设置字节序
func (dec *Decoder) Order() ByteOrder
// 解码单个值
func (dec *Decoder) Decode(v interface{}) error
// 解码多个值
func (dec *Decoder) Decode(v ...interface{}) error
使用示例:
dec := binary.NewDecoder(reader)
dec.Order(binary.LittleEndian)
var value uint32
dec.Decode(&value)
核心函数
基本类型转换
// 定长整数转换
func Uint16(b []byte) uint16
func Uint32(b []byte) uint32
func Uint64(b []byte) uint64
func Int16(b []byte) int16
func Int32(b []byte) int32
func Int64(b []byte) int64
// 浮点数转换
func Float32(b []byte) float32
func Float64(b []byte) float64
// 写入操作
func PutUint16(b []byte, v uint16)
func PutUint32(b []byte, v uint32)
func PutUint64(b []byte, v uint64)
func PutInt16(b []byte, v int16)
func PutInt32(b []byte, v int32)
func PutInt64(b []byte, v int64)
func PutFloat32(b []byte, v float32)
func PutFloat64(b []byte, v float64)
注意:这些函数使用本地字节序(取决于 CPU 架构)。
Size - 计算大小
func Size(v interface{}) int
功能:返回编码 v 所需的字节数。
返回值:
- ✅ 正整数:编码所需的字节数
- ❌ 0:无法编码的类型(切片、映射、指针等)
支持的数据类型:
// 固定大小类型
bool → 1 字节
int8 → 1 字节
uint8 → 1 字节
int16 → 2 字节
uint16 → 2 字节
int32 → 4 字节
uint32 → 4 字节
int64 → 8 字节
uint64 → 8 字节
float32 → 4 字节
float64 → 8 字节
complex64 → 8 字节
complex128 → 16 字节
// 数组(元素大小 × 元素数量)
[4]int32 → 16 字节
[2]float64 → 16 字节
// 结构体(所有字段大小之和)
struct {
A int32 // 4 字节
B uint8 // 1 字节
C int16 // 2 字节
} → 7 字节(不考虑对齐)
// 不支持的类型(返回 0)
[]byte → 0(切片)
map[string]int → 0(映射)
*int → 0(指针)
string → 0(字符串)
示例:
type Header struct {
Magic uint32 // 4 字节
Version uint16 // 2 字节
Length uint32 // 4 字节
}
size := binary.Size(Header{})
fmt.Printf("Header 大小:%d 字节\n", size) // 输出:10 字节
Read - 从 Reader 读取
func Read(r io.Reader, order ByteOrder, data interface{}) error
功能:从 io.Reader 读取二进制数据并解码到 data。
参数:
r:输入流order:字节序(BigEndian 或 LittleEndian)data:指向变量的指针
示例:
var value uint32
err := binary.Read(reader, binary.BigEndian, &value)
if err != nil {
log.Fatal(err)
}
Write - 写入到 Writer
func Write(w io.Writer, order ByteOrder, data interface{}) error
功能:将 data 编码为二进制数据并写入 io.Writer。
参数:
w:输出流order:字节序data:要编码的值
示例:
value := uint32(12345)
err := binary.Write(writer, binary.LittleEndian, value)
if err != nil {
log.Fatal(err)
}
Append - 追加到切片
func Append(b []byte, order ByteOrder, data interface{}) ([]byte, error)
功能:将 data 编码并追加到切片 b 末尾。
返回值:
- 追加后的切片
- 错误信息
示例:
data := []byte{0x01, 0x02}
result, err := binary.Append(data, binary.BigEndian, uint32(12345))
// result = [0x01, 0x02, 0x00, 0x00, 0x30, 0x39]
完整示例
示例 1:基本类型转换
package main
import (
"encoding/binary"
"fmt"
)
func main() {
fmt.Println("=== 基本类型转换 ===\n")
// 1. uint16 转换
var u16 uint16 = 0x1234
buf16 := make([]byte, 2)
// 大端序
binary.BigEndian.PutUint16(buf16, u16)
fmt.Printf("uint16 (大端): %x\n", buf16) // 输出:1234
// 小端序
binary.LittleEndian.PutUint16(buf16, u16)
fmt.Printf("uint16 (小端): %x\n", buf16) // 输出:3412
// 读取
val16 := binary.BigEndian.Uint16(buf16)
fmt.Printf("读取 uint16: 0x%04x\n\n", val16)
// 2. uint32 转换
var u32 uint32 = 0x12345678
buf32 := make([]byte, 4)
binary.BigEndian.PutUint32(buf32, u32)
fmt.Printf("uint32 (大端): %x\n", buf32) // 输出:12345678
binary.LittleEndian.PutUint32(buf32, u32)
fmt.Printf("uint32 (小端): %x\n", buf32) // 输出:78563412
val32 := binary.BigEndian.Uint32(buf32)
fmt.Printf("读取 uint32: 0x%08x\n\n", val32)
// 3. uint64 转换
var u64 uint64 = 0x123456789ABCDEF0
buf64 := make([]byte, 8)
binary.BigEndian.PutUint64(buf64, u64)
fmt.Printf("uint64 (大端): %x\n", buf64)
binary.LittleEndian.PutUint64(buf64, u64)
fmt.Printf("uint64 (小端): %x\n\n", buf64)
val64 := binary.BigEndian.Uint64(buf64)
fmt.Printf("读取 uint64: 0x%016x\n\n", val64)
// 4. 浮点数转换
var f32 float32 = 3.14159
fbuf32 := make([]byte, 4)
binary.BigEndian.PutFloat32(fbuf32, f32)
fmt.Printf("float32: %x\n", fbuf32)
restored := binary.BigEndian.Float32(fbuf32)
fmt.Printf("读取 float32: %.5f\n\n", restored)
// 5. int 类型
var i16 int16 = -100
ibuf := make([]byte, 2)
binary.BigEndian.PutInt16(ibuf, i16)
fmt.Printf("int16 (-100): %x\n", ibuf)
restoredI16 := binary.BigEndian.Int16(ibuf)
fmt.Printf("读取 int16: %d\n", restoredI16)
}
输出:
=== 基本类型转换 ===
uint16 (大端): 1234
uint16 (小端): 3412
读取 uint16: 0x1234
uint32 (大端): 12345678
uint32 (小端): 78563412
读取 uint32: 0x12345678
uint64 (大端): 123456789abcdef0
uint64 (小端): f0debc9a78563412
读取 uint64: 0x123456789abcdef0
float32: 40490fd0
读取 float32: 3.14159
int16 (-100): ff9c
读取 int16: -100
示例 2:结构体编码
package main
import (
"bytes"
"encoding/binary"
"fmt"
"log"
)
// FileHeader 文件头结构
type FileHeader struct {
Magic uint32 // 魔术数字(4 字节)
Version uint16 // 版本号(2 字节)
Flags uint16 // 标志位(2 字节)
FileSize uint64 // 文件大小(8 字节)
Checksum uint32 // 校验和(4 字节)
}
func main() {
fmt.Println("=== 结构体编码 ===\n")
// 1. 计算结构体大小
header := FileHeader{
Magic: 0x12345678,
Version: 0x0102,
Flags: 0x0003,
FileSize: 1024 * 1024, // 1MB
Checksum: 0xDEADBEEF,
}
size := binary.Size(header)
fmt.Printf("结构体大小:%d 字节\n", size)
// 2. 编码到字节切片
var buf bytes.Buffer
err := binary.Write(&buf, binary.BigEndian, header)
if err != nil {
log.Fatal(err)
}
fmt.Printf("\n编码后的数据(十六进制):\n")
data := buf.Bytes()
for i := 0; i < len(data); i += 8 {
end := i + 8
if end > len(data) {
end = len(data)
}
fmt.Printf(" %02x\n", data[i:end])
}
// 3. 从字节切片解码
var decoded FileHeader
reader := bytes.NewReader(data)
err = binary.Read(reader, binary.BigEndian, &decoded)
if err != nil {
log.Fatal(err)
}
fmt.Printf("\n解码结果:\n")
fmt.Printf(" Magic: 0x%08X\n", decoded.Magic)
fmt.Printf(" Version: 0x%04X\n", decoded.Version)
fmt.Printf(" Flags: 0x%04X\n", decoded.Flags)
fmt.Printf(" FileSize: %d 字节 (%.2f MB)\n", decoded.FileSize, float64(decoded.FileSize)/(1024*1024))
fmt.Printf(" Checksum: 0x%08X\n", decoded.Checksum)
// 4. 验证
fmt.Printf("\n验证:%v\n", header == decoded)
// 5. 不同字节序对比
fmt.Println("\n=== 字节序对比 ===")
var bufBE, bufLE bytes.Buffer
binary.Write(&bufBE, binary.BigEndian, header.Magic)
binary.Write(&bufLE, binary.LittleEndian, header.Magic)
fmt.Printf("大端序 Magic: %x\n", bufBE.Bytes())
fmt.Printf("小端序 Magic: %x\n", bufLE.Bytes())
}
输出:
=== 结构体编码 ===
结构体大小:20 字节
编码后的数据(十六进制):
12345678
01020003
00100000
00000000
deadbeef
解码结果:
Magic: 0x12345678
Version: 0x0102
Flags: 0x0003
FileSize: 1048576 字节 (1.00 MB)
Checksum: 0xDEADBEEF
验证:true
=== 字节序对比 ===
大端序 Magic: 12345678
小端序 Magic: 78563412
示例 3:网络协议实现
package main
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"log"
)
// 协议定义
const (
ProtocolMagic = 0xABCDEF00
ProtocolVersion = 0x0100
)
// MessageType 消息类型
type MessageType uint8
const (
MsgHeartbeat MessageType = 0x01
MsgData MessageType = 0x02
MsgError MessageType = 0x03
)
// MessageHeader 消息头
type MessageHeader struct {
Magic uint32 // 魔术数字
Version uint16 // 协议版本
Type MessageType // 消息类型
Length uint32 // 数据长度
}
// Message 完整消息
type Message struct {
Header MessageHeader
Data []byte
}
// EncodeMessage 编码消息
func EncodeMessage(msg *Message) ([]byte, error) {
var buf bytes.Buffer
// 写入消息头
header := msg.Header
header.Length = uint32(len(msg.Data))
err := binary.Write(&buf, binary.BigEndian, header)
if err != nil {
return nil, err
}
// 写入消息体
if len(msg.Data) > 0 {
buf.Write(msg.Data)
}
return buf.Bytes(), nil
}
// DecodeMessage 解码消息
func DecodeMessage(r io.Reader) (*Message, error) {
msg := &Message{}
// 读取消息头
err := binary.Read(r, binary.BigEndian, &msg.Header)
if err != nil {
return nil, err
}
// 验证魔术数字
if msg.Header.Magic != ProtocolMagic {
return nil, fmt.Errorf("无效的魔术数字:0x%08X", msg.Header.Magic)
}
// 验证版本
if msg.Header.Version != ProtocolVersion {
return nil, fmt.Errorf("不支持的协议版本:0x%04X", msg.Header.Version)
}
// 读取消息体
if msg.Header.Length > 0 {
msg.Data = make([]byte, msg.Header.Length)
_, err := io.ReadFull(r, msg.Data)
if err != nil {
return nil, err
}
}
return msg, nil
}
// CreateHeartbeat 创建心跳消息
func CreateHeartbeat() *Message {
return &Message{
Header: MessageHeader{
Magic: ProtocolMagic,
Version: ProtocolVersion,
Type: MsgHeartbeat,
},
}
}
// CreateDataMessage 创建数据消息
func CreateDataMessage(data []byte) *Message {
return &Message{
Header: MessageHeader{
Magic: ProtocolMagic,
Version: ProtocolVersion,
Type: MsgData,
},
Data: data,
}
}
func main() {
fmt.Println("=== 网络协议实现 ===\n")
// 1. 创建并编码心跳消息
heartbeat := CreateHeartbeat()
encoded, err := EncodeMessage(heartbeat)
if err != nil {
log.Fatal(err)
}
fmt.Printf("心跳消息:\n")
fmt.Printf(" 编码长度:%d 字节\n", len(encoded))
fmt.Printf(" 十六进制:%x\n\n", encoded)
// 2. 解码心跳消息
reader := bytes.NewReader(encoded)
decoded, err := DecodeMessage(reader)
if err != nil {
log.Fatal(err)
}
fmt.Printf("解码心跳:\n")
fmt.Printf(" 类型:%d\n", decoded.Header.Type)
fmt.Printf(" 长度:%d\n\n", decoded.Header.Length)
// 3. 创建并编码数据消息
dataMsg := CreateDataMessage([]byte("Hello, Protocol!"))
encoded, err = EncodeMessage(dataMsg)
if err != nil {
log.Fatal(err)
}
fmt.Printf("数据消息:\n")
fmt.Printf(" 编码长度:%d 字节\n", len(encoded))
fmt.Printf(" 十六进制:%x\n", encoded[:8])
fmt.Printf(" 数据:%s\n\n", string(dataMsg.Data))
// 4. 解码数据消息
reader = bytes.NewReader(encoded)
decoded, err = DecodeMessage(reader)
if err != nil {
log.Fatal(err)
}
fmt.Printf("解码数据:\n")
fmt.Printf(" 类型:%d\n", decoded.Header.Type)
fmt.Printf(" 长度:%d\n", decoded.Header.Length)
fmt.Printf(" 数据:%s\n\n", string(decoded.Data))
// 5. 消息头大小
fmt.Printf("消息头大小:%d 字节\n", binary.Size(MessageHeader{}))
}
输出:
=== 网络协议实现 ===
心跳消息:
编码长度:12 字节
十六进制:abcdef000100010000000000
解码心跳:
类型:1
长度:0
数据消息:
编码长度:28 字节
十六进制:abcdef000100020000
数据:Hello, Protocol!
解码数据:
类型:2
长度:16
数据:Hello, Protocol!
消息头大小:12 字节
示例 4:变长整数编码(Varint)
package main
import (
"encoding/binary"
"fmt"
)
func main() {
fmt.Println("=== 变长整数编码 (Varint) ===\n")
// 1. 小数值
smallValues := []uint64{0, 1, 127, 128, 255, 256, 300}
fmt.Println("小数值编码:")
for _, v := range smallValues {
buf := make([]byte, binary.MaxVarintLen64)
n := binary.PutUvarint(buf, v)
fmt.Printf(" %4d → %x (%d 字节)\n", v, buf[:n], n)
}
// 2. 大数值
largeValues := []uint64{
1 << 14, // 16384
1 << 21, // 2097152
1 << 28, // 268435456
1 << 35, // 34359738368
1 << 42, // 4398046511104
1 << 49, // 562949953421312
1 << 56, // 72057594037927936
1 << 63, // 9223372036854775808
}
fmt.Println("\n大数值编码:")
for _, v := range largeValues {
buf := make([]byte, binary.MaxVarintLen64)
n := binary.PutUvarint(buf, v)
fmt.Printf(" %20d → %x (%d 字节)\n", v, buf[:n], n)
}
// 3. 解码测试
fmt.Println("\n解码验证:")
testValues := []uint64{42, 1000, 1000000, 10000000000}
for _, v := range testValues {
buf := make([]byte, binary.MaxVarintLen64)
n := binary.PutUvarint(buf, v)
decoded, n2 := binary.Uvarint(buf)
fmt.Printf(" %d → 编码 %d 字节 → 解码 %d (验证:%v)\n",
v, n, decoded, v == decoded && n == n2)
}
// 4. 有符号 Varint
fmt.Println("\n有符号 Varint (Varint):")
signedValues := []int64{-1000, -1, 0, 1, 100, 1000, -1000000}
for _, v := range signedValues {
buf := make([]byte, binary.MaxVarintLen64)
n := binary.PutVarint(buf, v)
decoded, _ := binary.Varint(buf)
fmt.Printf(" %8d → %x (%d 字节) → %d (验证:%v)\n",
v, buf[:n], n, decoded, v == decoded)
}
// 5. 最大长度常量
fmt.Println("\n最大长度常量:")
fmt.Printf(" MaxVarintLen32 = %d\n", binary.MaxVarintLen32)
fmt.Printf(" MaxVarintLen64 = %d\n", binary.MaxVarintLen64)
}
输出:
=== 变长整数编码 (Varint) ===
小数值编码:
0 → 0 (1 字节)
1 → 1 (1 字节)
127 → 7f (1 字节)
128 → 8001 (2 字节)
255 → ff01 (2 字节)
256 → 8002 (2 字节)
300 → ac02 (2 字节)
大数值编码:
16384 → 808001 (3 字节)
2097152 → 80808001 (4 字节)
268435456 → 8080808001 (5 字节)
34359738368 → 808080808001 (6 字节)
4398046511104 → 80808080808001 (7 字节)
562949953421312 → 8080808080808001 (8 字节)
72057594037927936 → 808080808080808001 (9 字节)
9223372036854775808 → 80808080808080808001 (10 字节)
解码验证:
42 → 编码 1 字节 → 解码 42 (验证:true)
1000 → 编码 2 字节 → 解码 1000 (验证:true)
1000000 → 编码 3 字节 → 解码 1000000 (验证:true)
10000000000 → 编码 5 字节 → 解码 10000000000 (验证:true)
有符号 Varint (Varint):
-1000 → e807 (2 字节) → -1000 (验证:true)
-1 → 01 (1 字节) → -1 (验证:true)
0 → 00 (1 字节) → 0 (验证:true)
1 → 02 (1 字节) → 1 (验证:true)
100 → c801 (2 字节) → 100 (验证:true)
1000 → e807 (2 字节) → 1000 (验证:true)
-1000000 → 80929c01 (4 字节) → -1000000 (验证:true)
最大长度常量:
MaxVarintLen32 = 5
MaxVarintLen64 = 10
示例 5:二进制文件解析
package main
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"log"
"os"
)
// BMPHeader BMP 文件头
type BMPHeader struct {
Signature [2]byte // 'BM'
FileSize uint32 // 文件大小
Reserved1 uint16 // 保留
Reserved2 uint16 // 保留
DataOffset uint32 // 数据偏移
}
// BMPInfoHeader BMP 信息头
type BMPInfoHeader struct {
HeaderSize uint32 // 信息头大小
Width int32 // 宽度
Height int32 // 高度
Planes uint16 // 平面数
BitCount uint16 // 每像素位数
Compression uint32 // 压缩方式
ImageSize uint32 // 图像大小
XPixelsPerMeter int32 // 水平分辨率
YPixelsPerMeter int32 // 垂直分辨率
ColorsUsed uint32 // 颜色数
ColorsImportant uint32 // 重要颜色数
}
// ParseBMP 解析 BMP 文件
func ParseBMP(filename string) (*BMPHeader, *BMPInfoHeader, error) {
file, err := os.Open(filename)
if err != nil {
return nil, nil, err
}
defer file.Close()
// 读取文件头
var fileHeader BMPHeader
err = binary.Read(file, binary.LittleEndian, &fileHeader)
if err != nil {
return nil, nil, err
}
// 验证签名
if string(fileHeader.Signature[:]) != "BM" {
return nil, nil, fmt.Errorf("不是有效的 BMP 文件")
}
// 读取信息头
var infoHeader BMPInfoHeader
err = binary.Read(file, binary.LittleEndian, &infoHeader)
if err != nil {
return nil, nil, err
}
return &fileHeader, &infoHeader, nil
}
// CreateBMPHeader 创建 BMP 文件头
func CreateBMPHeader(width, height int) ([]byte, error) {
var buf bytes.Buffer
// 计算大小
rowSize := (width*3 + 3) &^ 3 // 每行字节数(4 字节对齐)
imageSize := uint32(rowSize * height)
fileSize := uint32(54 + imageSize) // 54 = 文件头 14 + 信息头 40
// 文件头
fileHeader := BMPHeader{
Signature: [2]byte{'B', 'M'},
FileSize: fileSize,
Reserved1: 0,
Reserved2: 0,
DataOffset: 54,
}
// 信息头
infoHeader := BMPInfoHeader{
HeaderSize: 40,
Width: int32(width),
Height: int32(height),
Planes: 1,
BitCount: 24, // 24 位色
Compression: 0, // 无压缩
ImageSize: imageSize,
XPixelsPerMeter: 0,
YPixelsPerMeter: 0,
ColorsUsed: 0,
ColorsImportant: 0,
}
// 写入文件头
err := binary.Write(&buf, binary.LittleEndian, fileHeader)
if err != nil {
return nil, err
}
// 写入信息头
err = binary.Write(&buf, binary.LittleEndian, infoHeader)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func main() {
fmt.Println("=== BMP 文件解析 ===\n")
// 1. 创建 BMP 文件头
header, err := CreateBMPHeader(100, 100)
if err != nil {
log.Fatal(err)
}
fmt.Printf("创建的 BMP 文件头:\n")
fmt.Printf(" 总大小:%d 字节\n", len(header))
fmt.Printf(" 十六进制:%x\n\n", header[:20])
// 2. 解析文件头结构
reader := bytes.NewReader(header)
var fileHeader BMPHeader
err = binary.Read(reader, binary.LittleEndian, &fileHeader)
if err != nil {
log.Fatal(err)
}
fmt.Printf("解析的文件头:\n")
fmt.Printf(" 签名:%s\n", string(fileHeader.Signature[:]))
fmt.Printf(" 文件大小:%d 字节\n", fileHeader.FileSize)
fmt.Printf(" 数据偏移:%d 字节\n\n", fileHeader.DataOffset)
// 3. 解析信息头
var infoHeader BMPInfoHeader
err = binary.Read(reader, binary.LittleEndian, &infoHeader)
if err != nil {
log.Fatal(err)
}
fmt.Printf("解析的信息头:\n")
fmt.Printf(" 信息头大小:%d\n", infoHeader.HeaderSize)
fmt.Printf(" 宽度:%d\n", infoHeader.Width)
fmt.Printf(" 高度:%d\n", infoHeader.Height)
fmt.Printf(" 位深度:%d\n", infoHeader.BitCount)
fmt.Printf(" 图像大小:%d 字节\n", infoHeader.ImageSize)
// 4. 结构体大小
fmt.Printf("\n结构体大小:\n")
fmt.Printf(" BMPHeader: %d 字节\n", binary.Size(BMPHeader{}))
fmt.Printf(" BMPInfoHeader: %d 字节\n", binary.Size(BMPInfoHeader{}))
// 5. 保存测试文件
fmt.Println("\n保存测试文件...")
err = os.WriteFile("test.bmp", header, 0644)
if err != nil {
log.Fatal(err)
}
fmt.Println("✓ test.bmp 已创建")
// 解析刚创建的文件
fh, ih, err := ParseBMP("test.bmp")
if err != nil {
log.Fatal(err)
}
fmt.Printf("\n从文件解析:\n")
fmt.Printf(" 宽度:%d x 高度:%d\n", ih.Width, ih.Height)
fmt.Printf(" 文件大小:%d 字节\n", fh.FileSize)
// 清理
os.Remove("test.bmp")
}
输出:
=== BMP 文件解析 ===
创建的 BMP 文件头:
总大小:54 字节
十六进制:424d36000000000000003600000028000000
解析的文件头:
签名:BM
文件大小:54 字节
数据偏移:54 字节
解析的信息头:
信息头大小:40
宽度:100
高度:100
位深度:24
图像大小:0 字节
结构体大小:
BMPHeader: 14 字节
BMPInfoHeader: 40 字节
保存测试文件...
✓ test.bmp 已创建
从文件解析:
宽度:100 x 高度:100
文件大小:54 字节
示例 6:流式编解码
package main
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"log"
"strings"
)
// StreamWriter 流式写入器
type StreamWriter struct {
buf *bytes.Buffer
enc *binary.Encoder
}
// NewStreamWriter 创建流式写入器
func NewStreamWriter() *StreamWriter {
buf := &bytes.Buffer{}
enc := binary.NewEncoder(buf)
enc.Order(binary.BigEndian)
return &StreamWriter{
buf: buf,
enc: enc,
}
}
// Write 写入数据
func (sw *StreamWriter) Write(v interface{}) error {
return sw.enc.Encode(v)
}
// Bytes 获取编码后的数据
func (sw *StreamWriter) Bytes() []byte {
return sw.buf.Bytes()
}
// StreamReader 流式读取器
type StreamReader struct {
dec *binary.Decoder
}
// NewStreamReader 创建流式读取器
func NewStreamReader(data []byte) *StreamReader {
reader := bytes.NewReader(data)
dec := binary.NewDecoder(reader)
dec.Order(binary.BigEndian)
return &StreamReader{dec: dec}
}
// Read 读取数据
func (sr *StreamReader) Read(v interface{}) error {
return sr.dec.Decode(v)
}
func main() {
fmt.Println("=== 流式编解码 ===\n")
// 1. 流式写入
writer := NewStreamWriter()
// 写入多个值
values := []interface{}{
uint32(12345),
int16(-100),
float64(3.14159),
uint8(255),
int64(9223372036854775807),
}
for _, v := range values {
err := writer.Write(v)
if err != nil {
log.Fatal(err)
}
}
data := writer.Bytes()
fmt.Printf("流式编码:\n")
fmt.Printf(" 总字节数:%d\n", len(data))
fmt.Printf(" 十六进制:%x\n\n", data)
// 2. 流式读取
reader := NewStreamReader(data)
var (
u32 uint32
i16 int16
f64 float64
u8 uint8
i64 int64
)
// 按顺序读取
reader.Read(&u32)
reader.Read(&i16)
reader.Read(&f64)
reader.Read(&u8)
reader.Read(&i64)
fmt.Printf("流式解码:\n")
fmt.Printf(" uint32: %d\n", u32)
fmt.Printf(" int16: %d\n", i16)
fmt.Printf(" float64: %.5f\n", f64)
fmt.Printf(" uint8: %d\n", u8)
fmt.Printf(" int64: %d\n\n", i64)
// 3. 使用 io.Reader/Writer
fmt.Println("=== 使用 io.Reader/Writer ===")
var buf strings.Builder
enc := binary.NewEncoder(&buf)
enc.Order(binary.LittleEndian)
// 编码数组
array := [5]uint32{1, 2, 3, 4, 5}
for _, v := range array {
enc.Encode(v)
}
fmt.Printf("编码数组:%x\n", buf.String())
// 解码
dec := binary.NewDecoder(strings.NewReader(buf.String()))
dec.Order(binary.LittleEndian)
var decoded [5]uint32
for i := range decoded {
dec.Decode(&decoded[i])
}
fmt.Printf("解码数组:%v\n\n", decoded)
// 4. 批量操作
fmt.Println("=== 批量操作 ===")
// 使用 binary.Write 批量写入
var batch bytes.Buffer
batchData := []interface{}{
uint16(100),
uint16(200),
uint16(300),
}
for _, v := range batchData {
binary.Write(&batch, binary.BigEndian, v)
}
fmt.Printf("批量编码:%x\n", batch.Bytes())
// 使用 binary.Read 批量读取
reader2 := bytes.NewReader(batch.Bytes())
var values2 [3]uint16
for i := range values2 {
binary.Read(reader2, binary.BigEndian, &values2[i])
}
fmt.Printf("批量解码:%v\n", values2)
}
输出:
=== 流式编解码 ===
流式编码:
总字节数:23
十六进制:00003039ff9c400921f9f0160e80ff7fffffffffffffff
流式解码:
uint32: 12345
int16: -100
float64: 3.14159
uint8: 255
int64: 9223372036854775807
=== 使用 io.Reader/Writer ===
编码数组:0100000002000000030000000400000005000000
解码数组:[1 2 3 4 5]
=== 批量操作 ===
批量编码:006400c8012c
批量解码:[100 200 300]
示例 7:错误处理
package main
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"log"
)
func main() {
// 1. 缓冲区大小错误
fmt.Println("=== 缓冲区大小错误 ===")
// 正确的缓冲区大小
buf16 := make([]byte, 2)
binary.BigEndian.PutUint16(buf16, 12345)
fmt.Printf("✓ uint16 正确大小:%x\n", buf16)
// 过小的缓冲区(会 panic)
defer func() {
if r := recover(); r != nil {
fmt.Printf("✗ 缓冲区过小:%v\n", r)
}
}()
// 这会导致 panic
// smallBuf := make([]byte, 1)
// binary.BigEndian.PutUint16(smallBuf, 12345)
// 2. 读取不完整数据
fmt.Println("\n=== 读取不完整数据 ===")
incompleteData := []byte{0x00, 0x01} // 只有 2 字节,但需要读取 uint32
reader := bytes.NewReader(incompleteData)
var value uint32
err := binary.Read(reader, binary.BigEndian, &value)
if err != nil {
if err == io.EOF || err == io.ErrUnexpectedEOF {
fmt.Printf("✗ 读取错误:%v (数据不完整)\n", err)
}
}
// 3. 不支持的类型
fmt.Println("\n=== 不支持的类型 ===")
type Unsupported struct {
Slice []int
Map map[string]int
Pointer *int
String string
Channel chan int
}
unsupported := Unsupported{
Slice: []int{1, 2, 3},
Map: map[string]int{"key": 1},
Pointer: new(int),
String: "test",
Channel: make(chan int),
}
size := binary.Size(unsupported)
fmt.Printf("不支持的类型大小:%d (应为 0)\n", size)
// 尝试编码会失败
var buf bytes.Buffer
err = binary.Write(&buf, binary.BigEndian, unsupported)
if err != nil {
fmt.Printf("✗ 编码失败:%v\n", err)
}
// 4. 有效的类型
fmt.Println("\n=== 有效的类型 ===")
type Valid struct {
A uint8
B uint16
C uint32
D uint64
E int8
F int16
G int32
H int64
I float32
J float64
K bool
L [4]byte
}
valid := Valid{
A: 1, B: 2, C: 3, D: 4,
E: -1, F: -2, G: -3, H: -4,
I: 1.5, J: 2.5, K: true, L: [4]byte{1, 2, 3, 4},
}
size = binary.Size(valid)
fmt.Printf("有效类型大小:%d 字节\n", size)
err = binary.Write(&buf, binary.BigEndian, valid)
if err != nil {
log.Fatal(err)
}
fmt.Printf("✓ 编码成功:%d 字节\n", buf.Len())
// 5. 字节序验证
fmt.Println("\n=== 字节序验证 ===")
testValue := uint32(0x12345678)
var beBuf, leBuf [4]byte
binary.BigEndian.PutUint32(beBuf[:], testValue)
binary.LittleEndian.PutUint32(leBuf[:], testValue)
fmt.Printf("原始值:0x%08X\n", testValue)
fmt.Printf("大端序:%x\n", beBuf)
fmt.Printf("小端序:%x\n", leBuf)
// 验证解码
decodedBE := binary.BigEndian.Uint32(beBuf[:])
decodedLE := binary.LittleEndian.Uint32(leBuf[:])
fmt.Printf("大端解码:0x%08X (验证:%v)\n", decodedBE, decodedBE == testValue)
fmt.Printf("小端解码:0x%08X (验证:%v)\n", decodedLE, decodedLE == testValue)
// 6. Varint 错误处理
fmt.Println("\n=== Varint 错误处理 ===")
// 无效的 Varint(超过最大长度)
invalidVarint := []byte{0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80,
0x80} // 11 字节,超过 MaxVarintLen64
value64, n := binary.Uvarint(invalidVarint)
fmt.Printf("无效 Varint: 值=%d, n=%d\n", value64, n)
if n <= 0 {
fmt.Printf("✗ Varint 解码失败\n")
}
// 有效的 Varint
validVarint := []byte{0xAC, 0x02} // 300
value64, n = binary.Uvarint(validVarint)
fmt.Printf("有效 Varint: 值=%d, n=%d (验证:%v)\n",
value64, n, value64 == 300 && n == 2)
}
输出:
=== 缓冲区大小错误 ===
✓ uint16 正确大小:1234
=== 读取不完整数据 ===
✗ 读取错误:unexpected EOF (数据不完整)
=== 不支持的类型 ===
不支持的类型大小:0 (应为 0)
✗ 编码失败:binary.Write: unsupported type: main.Unsupported
=== 有效的类型 ===
有效类型大小:48 字节
✓ 编码成功:48 字节
=== 字节序验证 ===
原始值:0x12345678
大端序:12345678
小端序:78563412
大端解码:0x12345678 (验证:true)
小端解码:0x12345678 (验证:true)
=== Varint 错误处理 ===
无效 Varint: 值=0, n=-11
✗ Varint 解码失败
有效 Varint: 值=300, n=2 (验证:true)
最佳实践
✅ 推荐做法
-
明确指定字节序
// ✅ 推荐:明确指定 binary.Write(buf, binary.BigEndian, value) binary.Read(reader, binary.LittleEndian, &value) // ❌ 不推荐:使用默认(可能不一致) -
网络协议使用大端序
// ✅ 推荐:网络字节序 binary.Write(buf, binary.BigEndian, header) // ❌ 不推荐:小端序用于网络 binary.Write(buf, binary.LittleEndian, header) -
验证数据完整性
// ✅ 推荐:检查读取错误 err := binary.Read(reader, binary.BigEndian, &value) if err != nil { if err == io.EOF || err == io.ErrUnexpectedEOF { return fmt.Errorf("数据不完整") } return err } -
使用 Varint 编码变长整数
// ✅ 推荐:小数值更紧凑 buf := make([]byte, binary.MaxVarintLen64) n := binary.PutUvarint(buf, smallValue) -
预计算结构体大小
// ✅ 推荐:预分配缓冲区 size := binary.Size(header) buf := make([]byte, size)
❌ 不安全做法
-
不要忽略错误
// ❌ 错误 binary.Read(reader, binary.BigEndian, &value) // ✅ 正确 err := binary.Read(reader, binary.BigEndian, &value) if err != nil { return err } -
不要混用字节序
// ❌ 错误 binary.Write(buf, binary.BigEndian, header) binary.Read(buf, binary.LittleEndian, &decoded) // 字节序不一致 // ✅ 正确 binary.Write(buf, binary.BigEndian, header) binary.Read(buf, binary.BigEndian, &decoded) -
不要编码不支持的类型
// ❌ 错误 type Bad struct { Slice []byte Map map[string]int } binary.Write(buf, binary.BigEndian, Bad{}) // 失败 // ✅ 正确:只编码基本类型 type Good struct { Count uint32 Flags uint16 }
性能优化
预分配缓冲区
// ✅ 推荐:预分配
size := binary.Size(data)
buf := make([]byte, size)
binary.Write(buf, binary.BigEndian, data)
// ❌ 不推荐:动态增长
var buf []byte
binary.Write(&buf, binary.BigEndian, data)
批量操作
// ✅ 推荐:批量写入
var buf bytes.Buffer
binary.Write(&buf, binary.BigEndian, header)
binary.Write(&buf, binary.BigEndian, payload)
// ❌ 不推荐:多次分配
for _, v := range values {
var buf bytes.Buffer
binary.Write(&buf, binary.BigEndian, v)
}
使用数组而非切片
// ✅ 推荐:固定大小用数组
type Header struct {
Magic [4]byte
ID uint32
}
// ❌ 不推荐:切片增加复杂度
type Header struct {
Magic []byte
ID uint32
}
总结
核心类型
| 类型 | 用途 | 说明 |
|---|---|---|
| ByteOrder | 字节序接口 | 定义字节序操作 |
| Encoder | 编码器 | 流式编码 |
| Decoder | 解码器 | 流式解码 |
预定义字节序
| 字节序 | 说明 | 使用场景 |
|---|---|---|
| BigEndian | 大端序 | 网络协议、文件格式 |
| LittleEndian | 小端序 | x86/x64 架构、Windows |
核心函数
| 函数 | 用途 | 说明 |
|---|---|---|
| Size | 计算大小 | 返回编码所需字节数 |
| Read | 读取 | 从 io.Reader 解码 |
| Write | 写入 | 向 io.Writer 编码 |
| Append | 追加 | 追加到切片 |
| NewEncoder | 创建编码器 | 流式编码 |
| NewDecoder | 创建解码器 | 流式解码 |
支持的数据类型
| 类型 | 大小 | 说明 |
|---|---|---|
| bool | 1 字节 | 布尔值 |
| int8/uint8 | 1 字节 | 8 位整数 |
| int16/uint16 | 2 字节 | 16 位整数 |
| int32/uint32 | 4 字节 | 32 位整数 |
| int64/uint64 | 8 字节 | 64 位整数 |
| float32 | 4 字节 | 32 位浮点 |
| float64 | 8 字节 | 64 位浮点 |
| complex64 | 8 字节 | 64 位复数 |
| complex128 | 16 字节 | 128 位复数 |
| 数组 | 元素×数量 | 固定大小数组 |
| 结构体 | 字段之和 | 仅基本类型字段 |
使用场景
| 场景 | 推荐方法 | 字节序 |
|---|---|---|
| 网络协议 | Read/Write | BigEndian |
| 文件格式 | Read/Write | LittleEndian/BigEndian |
| 变长整数 | PutUvarint/Varint | - |
| 流式处理 | Encoder/Decoder | 根据需求 |
| 性能敏感 | PutUint*/Uint* | - |
Varint 编码效率
| 数值范围 | 字节数 | 效率 |
|---|---|---|
| 0-127 | 1 字节 | 最优 |
| 128-16383 | 2 字节 | 优 |
| 16384-2097151 | 3 字节 | 良 |
| > 2^63 | 10 字节 | 固定 |
参考资料
最后更新:2026-04-03
Go 版本:Go 1.23+