Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

encoding/binary - 二进制编解码

概述

encoding/binary 包提供了二进制和 Go 值之间的互转功能。

encoding/binary 是什么

  • 📦 二进制序列化:将 Go 基本类型编码为字节序列
  • 🔧 字节序控制:支持大端(BigEndian)和小端(LittleEndian)
  • 📋 定长数据:处理固定长度的二进制数据
  • 🛠️ 底层操作:直接操作内存布局和字节序

主要用途

  • 🌐 网络协议:实现自定义网络协议
  • 📧 文件格式:解析和生成二进制文件格式
  • 🔐 加密解密:处理加密算法的字节数据
  • 📊 数据库:存储和读取二进制数据
  • 🖼️ 多媒体:解析图片、音频、视频格式
  • 🔑 系统编程:与硬件和系统接口交互

重要说明

  • ⚠️ 仅支持基本类型:只支持整数、浮点数等基本类型
  • ⚠️ 不支持切片和映射:不能直接编码复杂数据结构
  • ⚠️ 字节序敏感:必须明确指定字节序(大端或小端)
  • ⚠️ 内存对齐:注意结构体的内存对齐问题
  • 高性能:直接的内存操作,性能优异
  • 零拷贝:某些操作可以直接使用底层内存
  • 标准库支持:Go 标准库提供完整支持

与其他编码包的比较

编码格式可读性大小用途
encoding/binary二进制不可读最小底层数据、协议
encoding/jsonJSON 文本可读较大Web API、配置
encoding/gobGob 二进制不可读中等Go 程序间通信
encoding/xmlXML 文本可读最大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)

最佳实践

✅ 推荐做法

  1. 明确指定字节序

    // ✅ 推荐:明确指定
    binary.Write(buf, binary.BigEndian, value)
    binary.Read(reader, binary.LittleEndian, &value)
    
    // ❌ 不推荐:使用默认(可能不一致)
    
  2. 网络协议使用大端序

    // ✅ 推荐:网络字节序
    binary.Write(buf, binary.BigEndian, header)
    
    // ❌ 不推荐:小端序用于网络
    binary.Write(buf, binary.LittleEndian, header)
    
  3. 验证数据完整性

    // ✅ 推荐:检查读取错误
    err := binary.Read(reader, binary.BigEndian, &value)
    if err != nil {
        if err == io.EOF || err == io.ErrUnexpectedEOF {
            return fmt.Errorf("数据不完整")
        }
        return err
    }
    
  4. 使用 Varint 编码变长整数

    // ✅ 推荐:小数值更紧凑
    buf := make([]byte, binary.MaxVarintLen64)
    n := binary.PutUvarint(buf, smallValue)
    
  5. 预计算结构体大小

    // ✅ 推荐:预分配缓冲区
    size := binary.Size(header)
    buf := make([]byte, size)
    

❌ 不安全做法

  1. 不要忽略错误

    // ❌ 错误
    binary.Read(reader, binary.BigEndian, &value)
    
    // ✅ 正确
    err := binary.Read(reader, binary.BigEndian, &value)
    if err != nil {
        return err
    }
    
  2. 不要混用字节序

    // ❌ 错误
    binary.Write(buf, binary.BigEndian, header)
    binary.Read(buf, binary.LittleEndian, &decoded)  // 字节序不一致
    
    // ✅ 正确
    binary.Write(buf, binary.BigEndian, header)
    binary.Read(buf, binary.BigEndian, &decoded)
    
  3. 不要编码不支持的类型

    // ❌ 错误
    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创建解码器流式解码

支持的数据类型

类型大小说明
bool1 字节布尔值
int8/uint81 字节8 位整数
int16/uint162 字节16 位整数
int32/uint324 字节32 位整数
int64/uint648 字节64 位整数
float324 字节32 位浮点
float648 字节64 位浮点
complex648 字节64 位复数
complex12816 字节128 位复数
数组元素×数量固定大小数组
结构体字段之和仅基本类型字段

使用场景

场景推荐方法字节序
网络协议Read/WriteBigEndian
文件格式Read/WriteLittleEndian/BigEndian
变长整数PutUvarint/Varint-
流式处理Encoder/Decoder根据需求
性能敏感PutUint*/Uint*-

Varint 编码效率

数值范围字节数效率
0-1271 字节最优
128-163832 字节
16384-20971513 字节
> 2^6310 字节固定

参考资料


最后更新:2026-04-03
Go 版本:Go 1.23+