将[8]byte转换为uint64

huangapple go评论84阅读模式
英文:

Convert [8]byte to a uint64

问题

我遇到了一个非常奇怪的问题。我有一个长度为8的[]byte,是通过一些十六进制解码得到的。我需要将它转换为uint64以便使用。我尝试使用binary.Uvarint(),来自encoding/binary来实现,但它似乎只使用数组中的第一个字节。请考虑以下示例。

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上查看

运行时,它将num显示为0,即使在十六进制中,它应该是000108000801ab01。此外,如果从binary.Uvarint()中捕获第二个值,它是从缓冲区中读取的字节数,据我所知,应该是8,但实际上是1。

我是否理解错了?如果是这样,我应该使用什么替代方法?

谢谢大家。 将[8]byte转换为uint64

英文:

all. I'm encountering what seems to be a very strange problem. (It could be that it's far past when I should be asleep, and I'm overlooking something obvious.)

I have a []byte with length 8 as a result of some hex decoding. I need to produce a uint64 in order to use it. I have tried using binary.Uvarint(), from encoding/binary to do so, but it seems to only use the first byte in the array. Consider the following example.

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

Here it is on play.golang.org.

When that is run, it displays the num as 0, even though, in hex, it should be 000108000801ab01. Furthermore, if one catches the second value from binary.Uvarint(), it is the number of bytes read from the buffer, which, to my knowledge, should be 8, even though it is actually 1.

Am I interpreting this wrong? If so, what should I be using instead?

Thanks, you all. 将[8]byte转换为uint64

答案1

得分: 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)
}

英文:

You're decoding using a function whose use isn't the one you need :

> Varints are a method of encoding integers using one or more bytes;
> numbers with smaller absolute value take a smaller number of bytes.
> For a specification, see
> http://code.google.com/apis/protocolbuffers/docs/encoding.html.

It's not the standard encoding but a very specific, variable byte number, encoding. That's why it stops at the first byte whose value is less than 0x080.

As pointed by Stephen, binary.BigEndian and binary.LittleEndian provide useful functions to decode directly :

type ByteOrder interface {
    Uint16([]byte) uint16
    Uint32([]byte) uint32
    Uint64([]byte) uint64
    PutUint16([]byte, uint16)
    PutUint32([]byte, uint32)
    PutUint64([]byte, uint64)
    String() string
}

So you may use

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

or (if you want to check errors instead of panicking, thanks jimt for pointing this problem with the direct solution) :

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

答案2

得分: 2

如果不关心字节顺序,你可以尝试这样做:

arr := [8]byte{1,2,3,4,5,6,7,8}
num := *(*uint64)(unsafe.Pointer(&arr[0]))

http://play.golang.org/p/aM2r40ANQC

英文:

If don't care byte order, you can try this:

arr := [8]byte{1,2,3,4,5,6,7,8}
num := *(*uint64)(unsafe.Pointer(&arr[0]))

http://play.golang.org/p/aM2r40ANQC

答案3

得分: 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,依此类推。

英文:

If you look at the function for Uvarint you will see that it is not as straight a conversion as you expect.

To be honest, I haven't yet figured out what kind of byte format it expects (see edit).

But to write your own is close to trivial:

func Uvarint(buf []byte) (x uint64) {
	for i, b := range buf {
		x = x &lt;&lt; 8 + uint64(b)
		if i == 7 {
			return
		}
	}
	return
}

Edit

The byte format is none I am familiar.
It is a variable width encoding where the highest bit of each byte is a flag.
If set to 0, that byte is the last in the sequence.
If set to 1, the encoding should continue with the next byte.

Only the lower 7 bits of each byte are used to build the uint64 value. The first byte will set the lowest 7 bits of the uint64, the following byte bit 8-15, etc.

huangapple
  • 本文由 发表于 2012年12月1日 15:02:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/13657232.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定