天天看點

Golang Gob編碼各個類型的編解碼規則Gob提供的函數Encode和DecodeRegister和RegisterNameGebEncoder和GobDecoder後記

gob是golang包自帶的一個資料結構序列化的編碼/解碼工具。編碼使用encoder,解碼使用decoder。一種典型的應用場景就是rpc(remote procedure calls)。

gob和json的pack之類的方法一樣,由發送端使用encoder對資料結構進行編碼。在接收端收到消息之後,接收端使用decoder将序列化的資料變化成本地變量。

有一點需要注意,

結構體中預設的字段将不會被發送。而且在接收方,并不需要所有的字段都要有對應的結構屬性對應。godoc中的這個例子很形象:

Golang Gob編碼各個類型的編解碼規則Gob提供的函數Encode和DecodeRegister和RegisterNameGebEncoder和GobDecoder後記

當發送方傳遞的是struct{a, b int}結構的值的時候,接收方可以允許前9種結構,但是後面4種結構确實不允許的。

個人覺得這種設定是很符合邏輯的:接收端隻接受和發送資料“相似”的資料結構。允許模拟相似,但是不允許沖突。

整型:分為sign int和usign int, 其中從上面例子也看到,int和uint是不能互相編解碼的。float和int也是不能互相編解碼的。

struct,array,slice是可以被編碼的。但是function和channel是不能被編碼的。

bool類型是被當作uint來編碼的,0是false,1是true。

浮點類型的值都是被當作float64類型的值來編碼的

string和[]byte傳遞是uint(byte個數) + byte[]的形式編碼的

slice和array是按照uint(array個數) + 每個array編碼 這樣的形式進行編碼的

maps是按照 uint(map個數) + 鍵值對 這樣的形式進行編碼的

struct是按照一對對(屬性名 + 屬性值)來進行編碼的。其中屬性值是其自己對應的gob編碼。前面說過,如果有一個屬性值為0或空,則這個屬性直接被忽略。每個屬性的序号是由編碼時候順序決定的,從0開始順序遞增。struct在序列化前會以-1代表序列化的開始,以0代表序列化結束。即struct的序列化是按照 “-1 (0 屬性1名字 屬性1值) (1 屬性2名字 屬性2值) 0 ”來進行編碼的。

非常重要的一點:

這樣才能被包外的函數通路!!(謝謝treapdb提醒)

Golang Gob編碼各個類型的編解碼規則Gob提供的函數Encode和DecodeRegister和RegisterNameGebEncoder和GobDecoder後記

對于encoder和decoder可以看這個例子:

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

<code>package main</code>

<code>import (</code>

<code>    </code><code>"bytes"</code>

<code>    </code><code>"encoding/gob"</code>

<code>    </code><code>"fmt"</code>

<code>    </code><code>"log"</code>

<code>)</code>

<code>type p </code><code>struct</code> <code>{</code>

<code>    </code><code>x, y, z </code><code>int</code>

<code>    </code><code>name    </code><code>string</code>

<code>}</code>

<code>type q </code><code>struct</code> <code>{</code>

<code>    </code><code>x, y *int32</code>

<code>    </code><code>name </code><code>string</code>

<code>func main() {</code>

<code>    </code><code>var</code> <code>network bytes.buffer       </code>

<code>    </code><code>enc := gob.newencoder(&amp;network)</code>

<code>    </code><code>dec := gob.newdecoder(&amp;network)</code>

<code>    </code><code>// encode (send) the value.</code>

<code>    </code><code>err := enc.encode(p{3, 4, 5, </code><code>"pythagoras"</code><code>})</code>

<code>    </code><code>if</code> <code>err != nil {</code>

<code>        </code><code>log.fatal(</code><code>"encode error:"</code><code>, err)</code>

<code>    </code><code>}</code>

<code>    </code><code>// decode (receive) the value.</code>

<code>    </code><code>var</code> <code>q q</code>

<code>    </code><code>err = dec.decode(&amp;q)</code>

<code>        </code><code>log.fatal(</code><code>"decode error:"</code><code>, err)</code>

<code>    </code><code>fmt.println(q)</code>

<code>    </code><code>fmt.printf(</code><code>"%q: {%d,%d}\n"</code><code>, q.name, *q.x, *q.y)</code>

所有encoder和decoder的構造函數都有一個io結構,需要制定你将使用哪個io進行編碼解碼的傳輸。

這點在godoc中有說:

f e is nil, the value will be discarded. otherwise, the value underlying e must be a pointer to the correct type for the next data item received.

decode的參數如果不是nil,那就一定是一個指針了。

<code>    </code><code>var</code> <code>network bytes.buffer        </code><code>// stand-in for a network connection</code>

<code>    </code><code>enc := gob.newencoder(&amp;network) </code><code>// will write to network.</code>

<code>    </code><code>dec := gob.newdecoder(&amp;network) </code><code>// will read from network.</code>

<code>    </code><code>err := enc.encode(&amp;p{3, 4, 5, </code><code>"pythagoras"</code><code>})</code>

這個function也是沒有問題的。

這兩個方法是當編解碼中有一個字段是interface{}的時候需要對interface{}的可能産生的類型進行注冊。具體就看一下下面這個例子:

39

40

41

42

43

44

45

46

47

<code>    </code><code>name    </code><code>interface</code><code>{}</code>

<code>    </code><code>name </code><code>interface</code><code>{}</code>

<code>type inner </code><code>struct</code> <code>{</code>

<code>    </code><code>test </code><code>int</code>

<code>    </code> 

<code>    </code><code>gob.register(inner{})</code>

<code>    </code><code>inner := inner{1}</code>

<code>    </code><code>err := enc.encode(p{1,2,3, inner})</code>

這裡使用了gob.register(inner{})告訴系統:所有的interface是有可能為inner結構的。

在這個例子中,如果你注釋了gob.register, 系統會報錯。

registername是和register一樣的效果,隻是在register的同時也為這個類型附上一個别名。

這是兩個接口,如果你的資料結構實作了這兩個接口,當調用encoder.encode和decoder.decode的時候就會調用這兩個結構的對應函數

看一下下面這個例子:

<code>func (</code><code>this</code> <code>*p)gobencode() ([]</code><code>byte</code><code>, error) {</code>

<code>    </code><code>return</code> <code>[]</code><code>byte</code><code>{},nil</code>

這裡我的p實作了gobencoder接口,是以在enc.encode的時候會調用func (this *p)gobencode() ([]byte, error)

當然我這個函數直接傳回的是空byte,是以在解碼的時候會報錯:decode error:gob: type mismatch in decoder: want struct type main.q; got non-struct

這兩個接口暴露出來就代表你為自己定義的結構進行編解碼規則制定。當然,如果使用自己的編解碼規則,在編碼和解碼的過程就需要是一對的。

gob包是golang提供的“私有”的編解碼方式,文檔中也說了它的效率會比json,xml等更高(雖然我也沒有驗證)。是以在兩個go 服務之間的互相通信建議不要再使用json傳遞了,完全可以直接使用gob來進行資料傳遞。