1. encoding/binary包简介

在用Go进行数据传输的场景下,例如文件传输或文件存储时,需要将Go的数据例如int转换为[]byte。 得到的[]byte可以进一步在网络上传输或写入到文件中。这个场景需要借助go标准库中的encoding/binary包来实现。

encoding/binary包实现了简单的数字与字节序列的转换以及变长值的编解码。 一个定长值是指要么是固定长度的数字类型(int8, uint8, int16, float32, complex64, …)或者只包含定长值的结构体或者数组。

变长值是使用一到多个字节编码整数的方法,绝对值较小的数字会占用较少的字节数。这个在ProtocolBuffer的编码文档中有详细说明。

encoding/binary包相对于效率更注重简单。 如果需要高效的序列化,特别是数据结构较复杂的,可以选择更高级的解决方案, 例如encoding/gob包(Go语言自带的数据编码解码工具包),或者采用ProtocolBuffer(跨语言)。

2. 字节序

在使用encoding/binary包之前,先理解一下字节序的概念。

字节序也就是字节的顺序,具体是指多字节的数据的存放顺序,有两种方式:

  • 小端字节序(Little Endian): 低位字节排放在内存的低地址端,高位字节排放在内存的高地址端
  • 大端字节序(Big Endian): 高位字节排放在内存的低地址端,低位字节排放在内存的高地址端

简单理解:

例如对于一个64位整数15来说,由8个字节组成:

  • 小端字节序:低位字节在前,高位字节在后。15的字节序列是[15 0 0 0 0 0 0 0]
  • 大端字节序:高位字节在前,低位字节在后。15的字节序列是[0 0 0 0 0 0 0 15],大端字节序与我们读写数值的方法是一致的。

在进行数据传输时需要先考虑一下字节序,因为不同处理器的架构体系会使用不同的存储字节序,而对于TCP/IP网络传输的字节序则固定采用的是大端字节序。

3.代码示例

 1package main
 2
 3import (
 4	"bytes"
 5	"encoding/binary"
 6	"fmt"
 7	"log"
 8)
 9
10func main() {
11	var num int64 = 15 // 64位整数,8个字节
12	var buf bytes.Buffer
13	err := binary.Write(&buf, binary.BigEndian, num)
14	if err != nil {
15		log.Fatal()
16	}
17	bytes := buf.Bytes()
18	fmt.Println(bytes) // [0 0 0 0 0 0 0 15]
19	var decodingNum int64
20	err = binary.Read(&buf, binary.BigEndian, &decodingNum)
21	if err != nil {
22		log.Fatal()
23	}
24	fmt.Println(decodingNum) // 15
25
26	buf.Reset()
27	err = binary.Write(&buf, binary.LittleEndian, num)
28	if err != nil {
29		log.Fatal()
30	}
31	bytes = buf.Bytes()
32	fmt.Println(bytes) // [15 0 0 0 0 0 0 0]
33	err = binary.Read(&buf, binary.LittleEndian, &decodingNum)
34	if err != nil {
35		log.Fatal()
36	}
37	fmt.Println(decodingNum) // 15
38
39}

4.总结

使用encoding/binary包可以实现序列化和反序列化功能:

  • binary.Write这个函数可以将数据序列化成字节流
  • binary.Read这个函数可以将字节流反序列化为数据结构

使用encoding/binary包的优缺点:

  • 优点: 简单、高效
  • 缺点: 如果编码的结构中有不确定长度的类型,会报错

如果是go语言之间的序列化和反序列化推荐使用encoding/gob包,跨语言的序列化和反序列化可以使用protobuf。