所有。我遇到了一个似乎很奇怪的问题。 (可能是我睡着了很久了,而且我忽视了一些显而易见的事情。)
由于某些十六进制解码,我有一个长度为8的[]byte
。我需要生成一个uint64
才能使用它。我尝试使用来自encoding/binary
的{{3}}来执行此操作,但它似乎只使用数组中的第一个字节。请考虑以下示例。
package main
import (
"encoding/binary"
"fmt"
)
func main() {
array := []byte{0x00, 0x01, 0x08, 0x00, 0x08, 0x01, 0xab, 0x01}
num, _ := binary.Uvarint(array[0:8])
fmt.Printf("%v, %x\n", array, num)
}
运行时,它会将num
显示为0
,即使是十六进制,也应该是000108000801ab01
。此外,如果从binary.Uvarint()
捕获第二个值,它是从缓冲区读取的字节数,据我所知,它应该是8,即使它实际上是1。
我是否解释了这个错误?如果是这样,我应该使用什么呢?
谢谢,大家好。 :)
答案 0 :(得分:15)
您正在使用一种功能进行解码,该功能的使用不是您需要的功能:
Varints是一种使用一个或多个字节编码整数的方法; 绝对值较小的数字占用较少的字节数。 有关规范,请参阅 http://code.google.com/apis/protocolbuffers/docs/encoding.html
这不是标准编码,而是非常具体的可变字节数编码。这就是为什么它在第一个字节停止,其值小于0x080。
正如Stephen指出的那样,binary.BigEndian和binary.LittleEndian提供了直接解码的有用功能:
type ByteOrder interface {
Uint16([]byte) uint16
Uint32([]byte) uint32
Uint64([]byte) uint64
PutUint16([]byte, uint16)
PutUint32([]byte, uint32)
PutUint64([]byte, uint64)
String() string
}
所以你可以使用
package main
import (
"encoding/binary"
"fmt"
)
func main() {
array := []byte{0x00, 0x01, 0x08, 0x00, 0x08, 0x01, 0xab, 0x01}
num := binary.LittleEndian.Uint64(array)
fmt.Printf("%v, %x", array, num)
}
或(如果你想检查错误而不是恐慌,感谢jimt用直接解决方案指出这个问题):
package main
import (
"encoding/binary"
"bytes"
"fmt"
)
func main() {
array := []byte{0x00, 0x01, 0x08, 0x00, 0x08, 0x01, 0xab, 0x01}
var num uint64
err := binary.Read(bytes.NewBuffer(array[:]), binary.LittleEndian, &num)
fmt.Printf("%v, %x", array, num)
}
答案 1 :(得分:1)
如果您查看Uvarint的功能,您会发现它并不像您预期的那样直接转换。
说实话,我还没想出它所期望的字节格式(见编辑)。
但是写自己的东西是微不足道的:
func Uvarint(buf []byte) (x uint64) {
for i, b := range buf {
x = x << 8 + uint64(b)
if i == 7 {
return
}
}
return
}
修改强>
我不熟悉字节格式。 它是一种可变宽度编码,其中每个字节的最高位是一个标志 如果设置为0,则该字节是序列中的最后一个字节 如果设置为1,则编码应继续下一个字节。
仅使用每个字节的低7位来构建uint64值。第一个字节将设置uint64的最低7位,后面的字节位8-15等
答案 2 :(得分:1)
如果不关心字节顺序,可以试试这个:
arr := [8]byte{1,2,3,4,5,6,7,8}
num := *(*uint64)(unsafe.Pointer(&arr[0]))