Golang Gob編碼

Golang Gob編碼
1 Star2 Stars3 Stars4 Stars5 Stars 給文章打分!
Loading...

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

gob和json的pack之類的方法一樣,由傳送端使用Encoder對資料結構進行編碼。在接收端收到訊息之後,接收端使用Decoder將序列化的資料變化成本地變數。

 

有一點需要注意,

傳送方的結構和接受方的結構並不需要完全一致

結構體中預設的欄位將不會被髮送。而且在接收方,並不需要所有的欄位都要有對應的結構屬性對應。godoc中的這個例子很形象:

clip_image001

當傳送方傳遞的是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 ”來進行編碼的。

 

非常重要的一點:

Struct中的屬性應該是public的,即應該是大寫字母開頭。

這樣才能被包外的函式訪問!!(謝謝TreapDB提醒)

Gob提供的函式

clip_image002

 

Encode和Decode

對於Encoder和Decoder可以看這個例子:

package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type P struct {
X, Y, Z int
Name    string
}
type Q struct {
X, Y *int32
Name string
}
func main() {
var network bytes.Buffer        
enc := gob.NewEncoder(&network) 
dec := gob.NewDecoder(&network) 
// Encode (send) the value.
err := enc.Encode(P{3, 4, 5, "Pythagoras"})
if err != nil {
log.Fatal("encode error:", err)
}
// Decode (receive) the value.
var q Q
err = dec.Decode(&q)
if err != nil {
log.Fatal("decode error:", err)
}
fmt.Println(q)
fmt.Printf("%q: {%d,%d}\n", q.Name, *q.X, *q.Y)
}

所有Encoder和Decoder的建構函式都有一個io結構,需要制定你將使用哪個io進行編碼解碼的傳輸。

這個程式碼要注意幾個地方:

1 P和Q是兩個結構體,應該說是“相似”的兩個結構體

2 Encode是將結構體傳遞過來,但是Decode的函式引數卻是一個pointer!

這點在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,那就一定是一個指標了。

3 如果你將Encode傳入一個pointer,即

func main() {
var network bytes.Buffer        // Stand-in for a network connection
enc := gob.NewEncoder(&network) // Will write to network.
dec := gob.NewDecoder(&network) // Will read from network.
// Encode (send) the value.
err := enc.Encode(&P{3, 4, 5, "Pythagoras"})
if err != nil {
log.Fatal("encode error:", err)
}
// Decode (receive) the value.
var q Q
err = dec.Decode(&q)
if err != nil {
log.Fatal("decode error:", err)
}
fmt.Println(q)
fmt.Printf("%q: {%d,%d}\n", q.Name, *q.X, *q.Y)
}

這個function也是沒有問題的。

Register和RegisterName

這兩個方法是當編解碼中有一個欄位是interface{}的時候需要對interface{}的可能產生的型別進行註冊。具體就看一下下面這個例子:

package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type P struct {
X, Y, Z int
Name    interface{}
}
type Q struct {
X, Y *int32
Name interface{}
}
type Inner struct {
Test int
}
func main() {
var network bytes.Buffer        // Stand-in for a network connection
enc := gob.NewEncoder(&network) // Will write to network.
dec := gob.NewDecoder(&network) // Will read from network.
gob.Register(Inner{})
// Encode (send) the value.
inner := Inner{1}
err := enc.Encode(P{1,2,3, inner})
if err != nil {
log.Fatal("encode error:", err)
}
// Decode (receive) the value.
var q Q
err = dec.Decode(&q)
if err != nil {
log.Fatal("decode error:", err)
}
fmt.Println(q)
fmt.Printf("%q: {%d,%d}\n", q.Name, *q.X, *q.Y)
}

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

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

RegisterName是和Register一樣的效果,只是在Register的同時也為這個型別附上一個別名。

 

GebEncoder和GobDecoder

這是兩個介面,如果你的資料結構實現了這兩個介面,當呼叫encoder.Encode和decoder.Decode的時候就會呼叫這兩個結構的對應函式

看一下下面這個例子:

package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type P struct {
X, Y, Z int
Name    string
}
func (this *P)GobEncode() ([]byte, error) {
return []byte{},nil 
}
type Q struct {
X, Y *int32
Name string
}
func main() {
var network bytes.Buffer        
enc := gob.NewEncoder(&network) 
dec := gob.NewDecoder(&network) 
// Encode (send) the value.
err := enc.Encode(P{3, 4, 5, "Pythagoras"})
if err != nil {
log.Fatal("encode error:", err)
}
// Decode (receive) the value.
var q Q
err = dec.Decode(&q)
if err != nil {
log.Fatal("decode error:", err)
}
fmt.Println(q)
fmt.Printf("%q: {%d,%d}\n", q.Name, *q.X, *q.Y)
}

這裡我的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來進行資料傳遞。

參考資料

http://blog.golang.org/2011/03/gobs-of-data.html

http://www.mikespook.com/2011/03/%E3%80%90%E7%BF%BB%E8%AF%91%E3%80%91gob-%E7%9A%84%E6%95%B0%E6%8D%AE/


(adsbygoogle = window.adsbygoogle || []).push({});

function googleAdJSAtOnload() {
var element = document.createElement(“script”);
element.src = “//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js”;
element.async = true;
document.body.appendChild(element);
}
if (window.addEventListener) {
window.addEventListener(“load”, googleAdJSAtOnload, false);
} else if (window.attachEvent) {
window.attachEvent(“onload”, googleAdJSAtOnload);
} else {
window.onload = googleAdJSAtOnload;
}

程式語言 最新文章