猿问

编码/gob 是确定性的吗?

我们是否可以期望两个 Go 对象 x, y 使得 x 等于 y(假设没有接口和映射的技巧,只有结构和数组)gob_encode(x) 和 gob_encode(y) 的输出将始终相同?

edit (Jun 8 2018):

当涉及地图时,gob 编码是不确定的。这是由于地图的随机迭代顺序,导致它们的序列化是随机排序的。


陪伴而非守候
浏览 207回答 3
3回答

红颜莎娜

只要它“完成工作”,您就不应该真正在意。但是当前的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]序列。但是,如果您创建 2 个不同的gob.Encoders 并以相同的顺序写入相同的值,它们将产生精确的结果。请注意,在前面的语句中“相同顺序”也很重要。因为类型说明是在发送这种类型的第一个值时传输的,所以不同类型的值以不同的顺序发送也会以不同的顺序传输类型说明,因此类型的引用/标识符可能不同,这意味着当一个值此类类型已编码,将使用/发送不同类型的引用/ID。另请注意,gob包的实现可能会因发行版而异。这些更改将向后兼容(他们必须明确说明是否出于某种原因会进行向后不兼容的更改),但向后兼容并不意味着输出是相同的。所以不同的 Go 版本可能会产生不同的结果(但所有兼容版本都可以解码)。

慕哥9229398

可能应该注意的是,接受的答案是不正确的:编码/gob 不会以确定性的方式对地图元素进行排序:https : //play.golang.org/p/Hh3_5Kb3Znn我已经分叉了 encoding/gob 并添加了一些代码以在将它们写入流之前按键对地图进行排序。这会影响性能,但我的特定应用程序不需要高性能。请记住自定义封送拆收器可以打破这一点,所以小心使用:https : //github.com/dave/stablegob

米琪卡哇伊

如果您使用不同的类型和不同的编码器,它也不是确定性的。例子:package mainimport (    "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会产生不同的字节和不同的哈希码。尽管使用了不同的对象/指针,但还是会发生这种情况。
随时随地看视频慕课网APP

相关分类

Go
我要回答