将[8]byte转换为uint64

19
大家好。我遇到了一个非常奇怪的问题。(可能是因为已经过了我应该睡觉的时间,所以我忽略了一些明显的东西。)
我有一个长度为8的[]byte,这是一些十六进制解码的结果。我需要将其转换为uint64以便使用。我尝试使用encoding/binary中的binary.Uvarint()来实现,但似乎只使用了数组中的第一个字节。请考虑以下示例。
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)
}

这是在play.golang.org上的链接。

当运行时,它显示num0,尽管在十六进制中应该是000108000801ab01。此外,如果从binary.Uvarint()捕获第二个值,它是从缓冲区读取的字节数,根据我的了解,应该是8,但实际上是1。

我是否理解错了?如果是的话,我应该使用什么替代品?

谢谢大家。:)

3个回答

20

您正在使用的解码函数与您所需的功能不符:

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)
}

4
你可以通过使用num := binary.LittleEndian.Uint64(array)来避免使用Read()和bytes.Buffer。 - Stephen Weinberg
@StephenWeinberg +1 我不知道这一点。我用你的解决方案替换了我的,它更加简洁。 - Denys Séguret
2
需要注意的是,如果输入缓冲区不足以容纳完整请求的数据类型,则这些快捷方式方法将会出现错误。使用binary.Read方法可以返回一个error,您可以进行检查。 - jimt

2

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等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接