洋芋锅巴 發表於 2019-10-23 14:08:00

Go之gob包的使用

<p>gob包("encoding/gob")管理gob流——在encoder(编码器,也就是发送器)和decoder(解码器,也就是接受器)之间交换的字节流数据(gob 就是 go binary的缩写)。一般用于传递远端程序调用(RPC)的参数和结果。</p>
<p>要使用gob,通过调用NewEncoder()方法先创建一个编码器,并向其提供一系列数据;然后在接收端,通过调用NewDecoder()方法创建一个解码器,它从数据流中恢复数据并将它们填写进本地变量里。下面会通过几个例子进行说明。</p>
<p><span style="color: rgba(255, 0, 0, 1)">发送端和接收端的值/类型不需要严格匹配</span>。对结构体来说,某一字段(<span style="color: rgba(255, 0, 0, 1)">通过字段名进行识别</span>)如果发送端有而接收端没有,会被忽略;接收端有而发送端没有的字段也会被忽略;发送端和接收端都有的字段其类型必须是可兼容的;发送端和接收端都会在gob流和实际go类型之间进行必要的指针取址/寻址工作。举例如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">下面是发送端的承载数据的结构体:
</span><span style="color: rgba(0, 0, 255, 1)">struct</span> { A, B <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> }<br>
可以和如下类型互相发送和接收:
</span><span style="color: rgba(0, 0, 255, 1)">struct</span> { A, B <span style="color: rgba(0, 0, 255, 1)">int</span> }      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 同一类型</span>
*<span style="color: rgba(0, 0, 255, 1)">struct</span> { A, B <span style="color: rgba(0, 0, 255, 1)">int</span> }   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 结构体需要额外重定向(指针)</span>
<span style="color: rgba(0, 0, 255, 1)">struct</span> { *A, **B <span style="color: rgba(0, 0, 255, 1)">int</span> }   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 字段需要额外重定向(指针)</span>
<span style="color: rgba(0, 0, 255, 1)">struct</span> { A, B int64 }    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 同为整型/浮点型且符号类型相同的不同值类型</span>
<br><br><span style="color: rgba(0, 0, 0, 1)">可以发送给如下任一类型:
</span><span style="color: rgba(0, 0, 255, 1)">struct</span> { A, B <span style="color: rgba(0, 0, 255, 1)">int</span> }      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 同一类型</span>
<span style="color: rgba(0, 0, 255, 1)">struct</span> { B, A <span style="color: rgba(0, 0, 255, 1)">int</span> }      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 字段顺序改变无影响,按名称匹配</span>
<span style="color: rgba(0, 0, 255, 1)">struct</span> { A, B, C <span style="color: rgba(0, 0, 255, 1)">int</span> }   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 忽略多出的字段C</span>
<span style="color: rgba(0, 0, 255, 1)">struct</span> { B <span style="color: rgba(0, 0, 255, 1)">int</span> }         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 忽略缺少的字段A,会丢弃A的值</span>
<span style="color: rgba(0, 0, 255, 1)">struct</span> { B, C <span style="color: rgba(0, 0, 255, 1)">int</span> }      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 忽略缺少的字段A,忽略多出的字段C</span>
<br><br><span style="color: rgba(0, 0, 0, 1)">但尝试发送给如下类型的话就会导致错误:
</span><span style="color: rgba(0, 0, 255, 1)">struct</span> { A <span style="color: rgba(0, 0, 255, 1)">int</span>; B <span style="color: rgba(0, 0, 255, 1)">uint</span> }    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> B字段改变了符号类型</span>
<span style="color: rgba(0, 0, 255, 1)">struct</span> { A <span style="color: rgba(0, 0, 255, 1)">int</span>; B <span style="color: rgba(0, 0, 255, 1)">float</span> }   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> B字段改变了类型</span>
<span style="color: rgba(0, 0, 255, 1)">struct</span> { }                  <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 无共同字段名</span>
<span style="color: rgba(0, 0, 255, 1)">struct</span> { C, D <span style="color: rgba(0, 0, 255, 1)">int</span> }         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 无共同字段名</span></pre>
</div>
<p>&nbsp;</p>
<p>首先来看一个关于encode/decode结构体数据类型的示例。仔细观察这个例子,有助理解上面所说的发送端和接收端之间字段匹配的问题。</p>
<div class="cnblogs_code">
<pre>type P <span style="color: rgba(0, 0, 255, 1)">struct</span><span style="color: rgba(0, 0, 0, 1)"> {
    X, Y, Z </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">
    Name    </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
}

type Q </span><span style="color: rgba(0, 0, 255, 1)">struct</span><span style="color: rgba(0, 0, 0, 1)"> {
    X, Y </span>*<span style="color: rgba(0, 0, 0, 1)">int32
    Name </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
}

type R </span><span style="color: rgba(0, 0, 255, 1)">struct</span><span style="color: rgba(0, 0, 0, 1)"> {
    Y, W </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> This example shows the basic usage of the package: Create an encoder,
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> transmit some values, receive them with a decoder.</span>
<span style="color: rgba(0, 0, 0, 1)">func GobBasic() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始化 encoder 和 decoder</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> buf bytes.Buffer
    encoder :</span>= gob.NewEncoder(&amp;buf) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> will write to buf</span>
    decoder := gob.NewDecoder(&amp;buf) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> will read from buf

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Encode (send) some values</span>
    err := encoder.Encode(P{X: <span style="color: rgba(128, 0, 128, 1)">3</span>, Y: <span style="color: rgba(128, 0, 128, 1)">4</span>, Z: <span style="color: rgba(128, 0, 128, 1)">5</span>, Name: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">hello</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">})
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
      log.Fatal(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Encode error:</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,err)
    }<br>  
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> case 1<br>    // Decode (receive) and print the values
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">var q Q
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">err = decoder.Decode(&amp;q)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">if err != nil {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">    log.Fatal("Decode error:",err)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">}
    </span><span style="color: rgba(0, 128, 0, 1)">//
</span>    <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)">/ 注意,不能写成 q.X,因为在接收方,定义的是 int型 指针
    </span><span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)">/ *(q.X) 与 *q.Y 结果相同,但前者语义更加明确</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">fmt.Printf("%d %d %s\n", *(q.X), *q.Y, q.Name)

    </span><span style="color: rgba(0, 128, 0, 1)">//case 2<br></span><span style="color: rgba(0, 128, 0, 1)">    //var p P
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">err = decoder.Decode(&amp;p)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">if err != nil {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">    log.Fatal("Decode error:",err)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">}</span>
    <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)">/ 这里的接收方和传入方格式完全一致</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">fmt.Printf("%d %d %d %s\n", p.X, p.Y, p.Z, p.Name)</span>
<br>   // case 3
    <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> r R
    err </span>= decoder.Decode(&amp;<span style="color: rgba(0, 0, 0, 1)">r)
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
      log.Fatal(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Decode error:</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,err)
    }

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">fmt.Printf("%d %d %d %s\n", r.X, r.Y, r.Z, r.Name)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 会输出如下:因为接收端是根据字段名称进行匹配的
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> r.X undefined (type R has no field or method X)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> r.Z undefined (type R has no field or method Z)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> r.Name undefined (type R has no field or method Name)</span>
    fmt.Printf(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">%d\n</span><span style="color: rgba(128, 0, 0, 1)">"</span>, r.Y) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ok</span>
}</pre>
</div>
<p>&nbsp;</p>
<p>接着我们看一下encode/decode <span style="color: rgba(255, 0, 0, 1)">接口类型</span>的值是如何操作的。与其他常规的类型(比如结构体)最大的不同在于:<span style="color: rgba(255, 0, 0, 1)">需要注册一个明确的实现该接口的类型。</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">示例如下:</span></p>
<div class="cnblogs_code">
<pre>type Point <span style="color: rgba(0, 0, 255, 1)">struct</span><span style="color: rgba(0, 0, 0, 1)"> {
    X, Y </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">
}
func (p Point) Hypotenuse() float64 {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Hypot returns Sqrt(p*p + q*q)</span>
    <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> math.Hypot(float64(p.X), float64(p.Y))
}
type Pythagoras </span><span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> {
    Hypotenuse() float64
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这个例子展示了如何 encode/decode 一个接口类型(interface{})的值
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 与其他常规的类型(比如结构体)最大的不同在于:
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 需要注册一个明确的实现该接口的类型</span>
<span style="color: rgba(0, 0, 0, 1)">func GobInterface(){
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 我们必须要对encoder和decoder注册具体的类型,
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 因为通常来说,decoder和encoder是在不同的机器上的。
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 经过“注册”,解析引擎才能知道实现这一接口的具体类型是什么
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> (因为同一个接口可以有多种不同的实现)</span>
<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(255, 0, 0, 1)">    gob.Register(Point{})</span>

    p1 :</span>= Point{X: <span style="color: rgba(128, 0, 128, 1)">3</span>, Y: <span style="color: rgba(128, 0, 128, 1)">4</span><span style="color: rgba(0, 0, 0, 1)">}
    fmt.Println(p1.Hypotenuse()) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 5
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 编码,再解码,观察解码后返回的结果是否一致</span>
    b, _ :=<span style="color: rgba(0, 0, 0, 1)"> encode(p1)
    p2, _ :</span>=<span style="color: rgba(0, 0, 0, 1)"> decode(b)
    fmt.Println(p2.Hypotenuse()) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 5</span>
<span style="color: rgba(0, 0, 0, 1)">}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 编码,把结构体数据编码成字节流数据</span>
func encode(p Pythagoras) ([]<span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">, error) {
    </span><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> buf bytes.Buffer
    encoder :</span>= gob.NewEncoder(&amp;buf) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 构造编码器,并把数据写进buf中</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span> err := encoder.Encode(&amp;p); err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
      log.Printf(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">encode error: %v\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, err)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> nil, err
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> buf.Bytes(), nil
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 解码,把字节流数据解析成结构体数据</span>
func decode(b []<span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">) (Pythagoras, error) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">var buf bytes.Buffer</span>
    bufPtr := bytes.NewBuffer(b)      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回的类型是 *Buffer,而不是 Buffer。注意一下</span>
    decoder := gob.NewDecoder(bufPtr) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从 bufPtr 中获取数据</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> p Pythagoras
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> err := decoder.Decode(&amp;p); err != nil { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将数据写进变量 p 中</span>
      <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Point{}, err
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> p, nil
}</span></pre>
</div>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/kkbill/p/11725966.html
頁: [1]
查看完整版本: Go之gob包的使用