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.代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
)

func main() {
	var num int64 = 15 // 64位整数,8个字节
	var buf bytes.Buffer
	err := binary.Write(&buf, binary.BigEndian, num)
	if err != nil {
		log.Fatal()
	}
	bytes := buf.Bytes()
	fmt.Println(bytes) // [0 0 0 0 0 0 0 15]
	var decodingNum int64
	err = binary.Read(&buf, binary.BigEndian, &decodingNum)
	if err != nil {
		log.Fatal()
	}
	fmt.Println(decodingNum) // 15

	buf.Reset()
	err = binary.Write(&buf, binary.LittleEndian, num)
	if err != nil {
		log.Fatal()
	}
	bytes = buf.Bytes()
	fmt.Println(bytes) // [15 0 0 0 0 0 0 0]
	err = binary.Read(&buf, binary.LittleEndian, &decodingNum)
	if err != nil {
		log.Fatal()
	}
	fmt.Println(decodingNum) // 15

}

4.总结

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

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

使用encoding/binary包的优缺点:

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

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