prototext format 随机空格
- 问题简述
- 代码
- 复现
- 解密
- 结论
- 总结
问题简述
golang 语言,在使用 prototext 进行 format 的时候,相同的代码输出结果不唯一,有的时候字段之间是两个空格,有的时候是一个空格。
代码
先上pb文件
syntax = "proto3";
package codepractice.tmp;
option go_package = "codepractice/tmp";
service manage {
rpc Hello(HelloRequest) returns (HelloReply) {}
}
enum HelloType {
HELLO_TYPE_UNSPECIFIED = 0;
SHAKE_HANDS = 1;
EMBRACE = 2;
}
message HelloRequest {
HelloType type = 1;
string name = 2;
}
message HelloReply {}
可以看出来pb文件定义的东西很少,只有一个Hello的rpc接口,一个HelloRequest,里面有一个type字段和name字段,一个空的HelloReply。
接下来上代码
package tmp_test
import (
"testing"
"test/codepractice/tmp"
"google.golang.org/protobuf/encoding/prototext"
)
func Test_Hello(t *testing.T) {
hello := &tmp.HelloRequest{
Type: tmp.HelloType_EMBRACE,
Name: "jake",
}
str := prototext.MarshalOptions{Multiline: false}.Format(hello)
str2 := `type:EMBRACE name:"jake"`
// t.Log("hahanicai")
if str != str2 {
t.Fatalf("not equal %s, %s\n", str, str2)
}
t.Log("equal")
}
代码先初始化了一个HelloRequest结构体,然后使用prototext进行format。再定义一个名为 str2 的字符串,如果两个字符串不相等输出 not equal,否则输出 equal。
复现
这段代码很简单,讲道理str和str2肯定是相等的,但是在实际运行当中str和str2是不相等的,程序输出为
=== RUN Test_Hello
hello_test.go:23: not equal type:EMBRACE name:"jake", type:EMBRACE name:"jake"
--- FAIL: Test_Hello (0.00s)
FAIL
通过输出可以看到使用prototext format后的str,type字段和name字段中间有两个空格,那么是否可以将str2中也改成两个空格就可以呢。
如果我们把代码中注释了的t.Log(“hahanicai”)这句删除掉,再运行代码
=== RUN Test_Hello
hello_test.go:23: equal
--- PASS: Test_Hello (0.00s)
PASS
可以看到是成功了的结果,也就是字段之间是一个空格,那为什么一句注释会影响代码执行结果呢。
解密
通过翻阅prototext的源码可以看到,在format的时候会有一个bool变量的判断,来决定是否要添加一个多余的空格
从注释中也可以看出来,这个bool值是随机生成的,detrand.Bool 里面使用的 hash 计算逻辑和二进制文件的大小以及开头的内容有关
所以才会出现一句注释会影响代码执行结果的离谱情况。
结论
prototext 就是只想让你用这个 text 去给人看,而不是给机器存或者进行对比才会有随机空格的加入
程序编译好后,代码执行结果是稳定的,但是增加某一行重新编译后,代码结果就可能会变
总结
不要使用 prototext 进行数据的存储或者对比,但是可以用来打印log输出。