Go编程的三十六个套路: int与[]byte互转用于数据传输
2016-03-01
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。