将整数转换为字节数组

71

我有一个函数,它接收一个[]byte,但是我手头有的是一个int,如何最好地进行转换?

err = a.Write([]byte(myInt))

我想我可以走弯路,将其转换为字符串,并将其放入字节中,但这听起来很丑陋,我想有更好的方法来解决它。

13个回答

79

我同意Brainstorm的做法:假设您正在传递机器友好的二进制表示,则使用encoding/binary库。 OP建议binary.Write()可能有一些开销。查看Write()实现的源代码后,我发现它会进行一些运行时决策以实现最大的灵活性。

func Write(w io.Writer, order ByteOrder, data interface{}) error {
    // Fast path for basic types.
    var b [8]byte
    var bs []byte
    switch v := data.(type) {
    case *int8:
        bs = b[:1]
        b[0] = byte(*v)
    case int8:
        bs = b[:1]
        b[0] = byte(v)
    case *uint8:
        bs = b[:1]
        b[0] = *v
    ...

对吗?Write()接受一个非常通用的第三个参数data,这会强制Go运行时对类型信息进行编码,从而增加一些开销。由于Write()在这里做了一些运行时决策,而在您的情况下并不需要,也许您可以直接调用编码函数,看看它是否执行得更好。

就像这样:

package main

import (
    "encoding/binary"
    "fmt"
)

func main() {
    bs := make([]byte, 4)
    binary.LittleEndian.PutUint32(bs, 31415926)
    fmt.Println(bs)
}

告诉我们这个的表现如何。

否则,如果您只是想获取整数的ASCII表示,您可以获得字符串表示形式(可能使用strconv.Itoa),并将该字符串强制转换为[]byte类型。

package main

import (
    "fmt"
    "strconv"
)

func main() {
    bs := []byte(strconv.Itoa(31415926))
    fmt.Println(bs)
}

最终我采用了你第一个建议的小变化。本应该自己深入实现,感谢你的提示。 - vascop

35

8
在对“编码/二进制”方式进行基准测试后,它所需的时间几乎比int -> string -> byte长4倍。 - vascop
6
你的消费者期望整数的字节表示形式是什么?肯定不是ASCII表示形式吧?请澄清。 - dyoo
1
@vascop 是的。但是 binary.LittleEndian.PutUint64(bs, value) 比 int -> string -> byte 快得多。 - Kostub Deshmukh
2
一定要检查binary.Write返回的错误,参见@Agniva De Sarker的答案。不能直接使用int类型,需要将其强制转换为int64或类似类型。 - aleb

27

抱歉,可能有些晚了。但我认为在Go文档中找到了更好的实现方式。

buf := new(bytes.Buffer)
var num uint16 = 1234
err := binary.Write(buf, binary.LittleEndian, num)
if err != nil {
    fmt.Println("binary.Write failed:", err)
}
fmt.Printf("% x", buf.Bytes())

此答案应位于顶部,它是唯一有意义的。 - Tchakabam

3

我原本认为int类型有获取int哈希到字节的任何方法,但是我首先发现了用于此的math / big方法。 https://golang.org/pkg/math/big/

var f int = 52452356235;          // int
var s = big.NewInt(int64(f))      // int to big Int
var b = s.Bytes()                 // big Int to bytes
// b - byte slise
var r = big.NewInt(0).SetBytes(b) // bytes to big Int
var i int = int(r.Int64())        // big Int to int

https://play.golang.org/p/VAKSGw8XNQq

然而,这种方法使用的是绝对值。 如果你再花费一个字节,就可以传输符号。

func IntToBytes(i int) []byte{
    if i > 0 {
        return append(big.NewInt(int64(i)).Bytes(), byte(1))
    }
    return append(big.NewInt(int64(i)).Bytes(), byte(0))
}
func BytesToInt(b []byte) int{
    if b[len(b)-1]==0 {
        return -int(big.NewInt(0).SetBytes(b[:len(b)-1]).Int64())
    }
    return int(big.NewInt(0).SetBytes(b[:len(b)-1]).Int64())
}

https://play.golang.org/p/mR5Sp5hu4jk

或者 new(https://play.golang.org/p/7ZAK4QL96FO)

(该包还提供了将数据填充到现有切片中的函数)

https://golang.org/pkg/math/big/#Int.FillBytes


2
这是另一种选项,基于 Go 源代码[1]:
package main

import (
   "encoding/binary"
   "fmt"
   "math/bits"
)

func encodeUint(x uint64) []byte {
   buf := make([]byte, 8)
   binary.BigEndian.PutUint64(buf, x)
   return buf[bits.LeadingZeros64(x) >> 3:]
}

func main() {
   for x := 0; x <= 64; x += 8 {
      buf := encodeUint(1<<x-1)
      fmt.Println(buf)
   }
}

结果:

[]
[255]
[255 255]
[255 255 255]
[255 255 255 255]
[255 255 255 255 255]
[255 255 255 255 255 255]
[255 255 255 255 255 255 255]
[255 255 255 255 255 255 255 255]

比math/big快得多:
BenchmarkBig-12         28348621                40.62 ns/op
BenchmarkBit-12         731601145                1.641 ns/op
  1. https://github.com/golang/go/blob/go1.16.5/src/encoding/gob/encode.go#L113-L117

1
你可以尝试使用 musgo_int 来进行转换。你只需要将你的变量传入即可:
package main

import (
    "github.com/ymz-ncnk/musgo_int"
)

func main() {
    var myInt int = 1234

    // from int to []byte
    buf := make([]byte, musgo_int.Int(myInt).SizeMUS())
    musgo_int.Int(myInt).MarshalMUS(buf)

    // from []byte to int
    _, err := (*musgo_int.Int)(&myInt).UnmarshalMUS(buf)
    if err != nil {
        panic(err)
    }
}

1

添加此选项以处理基本的uint8到byte[]转换

foo := 255 // 1 - 255
ufoo := uint16(foo) 
far := []byte{0,0}
binary.LittleEndian.PutUint16(far, ufoo)
bar := int(far[0]) // back to int
fmt.Println("foo, far, bar : ",foo,far,bar)

输出:

foo, far, bar : 255 [255 0] 255


0

0

将整数转换为字节切片。

import (
    "bytes"
    "encoding/binary"
    "log"
)

func IntToBytes(num int64) []byte {
    buff := new(bytes.Buffer)
    bigOrLittleEndian := binary.BigEndian
    err := binary.Write(buff, bigOrLittleEndian, num)
    if err != nil {
        log.Panic(err)
    }

    return buff.Bytes()
}

0
第一步。忽略字节顺序,直接对任何类型进行操作:
func toBytes[T any](val T) []byte {
    return unsafe.Slice((*byte)(unsafe.Pointer(&val)), unsafe.Sizeof(val))
}

(还可以考虑在类型T上添加整数约束
第二步。你真的需要在具有不同字节顺序的机器之间实现可移植性吗?好的,这里有一个想法:
func toLEBytes[T any](val T) []byte {
    bytes := toBytes(val)
    if runningOnBigEndian() {
        slices.Reverse(bytes)
    }
    return bytes
}

你需要做的就是实现runningOnBigEndian()函数。首先阅读这个问题的答案: 有没有更好的方法来检查Go语言的字节序
另外请注意,反转字节是可以的,因为字节序是字节的顺序或序列,而不是位。

Endian example


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