英文:
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)
}
运行时,它将num
显示为0
,即使在十六进制中,它应该是000108000801ab01
。此外,如果从binary.Uvarint()
中捕获第二个值,它是从缓冲区中读取的字节数,据我所知,应该是8,但实际上是1。
我是否理解错了?如果是这样,我应该使用什么替代方法?
谢谢大家。
英文:
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.
答案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]))
答案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 << 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论