我们可以期望两个Go对象x,y使得x等于y(假设没有接口和映射,只是结构和数组的技巧)gob_encode(x)和gob_encode(y)的输出将始终是一样吗?
编辑(2018年6月8日):
当涉及地图时,gob编码非确定性。这是由于地图的随机迭代顺序,导致它们的序列化是随机排序的。
答案 0 :(得分:5)
只要“完成工作”,你就不应该真正关心。但目前的encoding/gob
实施是确定性的。但是(继续阅读)!
自:
gobs流是自我描述的。流中的每个数据项前面都有一个类型的规范,用一小组预定义类型表示。
这意味着如果您是第一次对类型的值进行编码,则会发送类型信息。如果您对另一个相同类型的值进行编码,则不会再次传输类型描述,只是对其先前规范的引用。因此,即使您对相同的值进行两次编码,它也会生成不同的字节序列,因为第一个将包含类型规范和值,第二个将仅包含类型ref(例如类型ID)和值。
见这个例子:
type Int struct{ X int }
b := &bytes.Buffer{}
e := gob.NewEncoder(b)
e.Encode(Int{1})
fmt.Println(b.Bytes())
e.Encode(Int{1})
fmt.Println(b.Bytes())
e.Encode(Int{1})
fmt.Println(b.Bytes())
输出(在Go Playground上尝试):
[23 255 129 3 1 1 3 73 110 116 1 255 130 0 1 1 1 1 88 1 4 0 0 0 5 255 130 1 2 0]
[23 255 129 3 1 1 3 73 110 116 1 255 130 0 1 1 1 1 88 1 4 0 0 0 5 255 130 1 2 0 5 255 130 1 2 0]
[23 255 129 3 1 1 3 73 110 116 1 255 130 0 1 1 1 1 88 1 4 0 0 0 5 255 130 1 2 0 5 255 130 1 2 0 5 255 130 1 2 0]
如第一个Encode()
生成大量字节加上Int
值为[5 255 130 1 2 0]
的值,第二次和第三次调用会添加相同的[5 255 130 1 2 0]
序列。< / p>
但是如果您创建2个不同的gob.Encoder
并且您以相同的顺序编写相同的值,它们将产生精确的结果。
请注意,在之前的声明中,“相同顺序”也很重要。因为在发送这种类型的第一个值时发送类型规范,所以以不同顺序发送不同类型的值也将以不同的顺序发送类型规范,因此类型的引用/标识符可能不同,这意味着当值为0时这种类型被编码,将使用/发送不同类型的引用/ id。
另请注意,gob
包的实现可能会在发行版之间发生变化。这些更改将向后兼容(如果由于某种原因,它们必须明确说明它们会进行向后不兼容的更改),但向后兼容并不意味着输出是相同的。因此,不同的Go版本可能会产生不同的结果(但所有兼容版本都可以解码)。
答案 1 :(得分:1)
应该注意的是,接受的答案是不正确的:encoding / gob不会以确定的方式对地图元素进行排序:https://play.golang.org/p/Hh3_5Kb3Znn
我已经分叉编码/ gob并添加了一些代码来按键排序地图,然后再将它们写入流中。这会影响性能,但我的特定应用程序不需要高性能。请记住,自定义封送程序可以解决此问题,因此请谨慎使用:https://github.com/dave/stablegob
答案 2 :(得分:0)
使用不同的类型和不同的编码器也不确定。
示例:
package main
import (
"bytes"
"crypto/sha1"
"encoding/gob"
"encoding/hex"
"log"
)
func main() {
encint()
encint64()
encstring()
}
func encint() {
s1 := []int{0, 2, 4, 5, 7}
buf2 := bytes.Buffer{}
enc2 := gob.NewEncoder(&buf2)
enc2.Encode(s1)
}
func encint64() {
s1 := []int64{0, 2, 4, 5, 7}
buf2 := bytes.Buffer{}
enc2 := gob.NewEncoder(&buf2)
enc2.Encode(s1)
}
func encstring() {
s1 := []string{"a", "b", "c", "d"}
buf2 := bytes.Buffer{}
enc2 := gob.NewEncoder(&buf2)
enc2.Encode(s1)
log.Println(buf2.Bytes())
hash := sha1.New()
hash.Write(buf2.Bytes())
ret := hash.Sum(nil)
log.Println(hex.EncodeToString(ret))
}
在Go Playground中运行
请注意,如果您注释掉encint()
或encint64()
,则encstring
将产生不同的字节和不同的哈希码。
即使使用不同的对象/指针,也会发生这种情况。