我们能否期望两个 Go 对象 x, y 使得 x 等于 y(假设接口和映射没有技巧,只是结构和数组)gob_encode(x) 和 gob_encode(y) 的输出将始终相同?
编辑(2018 年 6 月 8 日):
当涉及地图时, gob 编码是不确定的。这是由于地图的随机迭代顺序,导致它们的序列化是随机排序的。
只要它“完成工作”,您就不必真正关心它。但是当前的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
value 的值[5 255 130 1 2 0]
,第二个和第三个调用添加相同的[5 255 130 1 2 0]
序列。
但是,如果您创建 2 个不同gob.Encoder
的 s 并以相同的顺序写入相同的值,它们将产生精确的结果。
请注意,在前面的陈述中,“相同的顺序”也很重要。因为类型规范是在发送该类型的第一个值时传输的,所以以不同的顺序发送不同类型的值也会以不同的顺序传输类型规范,因此类型的引用/标识符可能会有所不同,这意味着当一个值这种类型被编码,将使用/发送不同类型的引用/id。
另请注意,gob
包的实现可能会因版本而异。这些更改将是向后兼容的(如果由于某种原因它们会进行向后不兼容的更改,它们必须明确说明),但向后兼容并不意味着输出是相同的。所以不同的 Go 版本可能会产生不同的结果(但所有兼容版本都可以解码)。
可能应该指出,接受的答案是不正确的: encoding/gob 不会以确定的方式对地图元素进行排序:https: //play.golang.org/p/Hh3_5Kb3Znn
在将它们写入流之前,我已经分叉了编码/gob 并添加了一些代码来按键排序地图。这会影响性能,但我的特定应用程序不需要高性能。请记住自定义编组器可以打破这一点,因此请小心使用:https ://github.com/dave/stablegob
如果您使用不同的类型和不同的编码器,它也不是确定性的。
例子:
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))
}
请注意,如果您注释掉encint()
或encint64()
将encstring
产生不同的字节和不同的哈希码。
尽管使用了不同的对象/指针,但仍会发生这种情况。