在Go语言中将int32转换为字节数组

12

我正在使用 big.NewInt(int64(e)).Bytes() 将 int32 转换为字节数组。

有没有更优雅的方法来实现这个?

我期望 AQAB 是 e 的 Base64 编码值。

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

const e = 65537

func base64Encode(b []byte) string {
  return strings.TrimRight(base64.StdEncoding.EncodeToString(b), "=")
}

func main() {
  fmt.Printf("exp %d\n", e)

  b := make([]byte, 4)
  binary.BigEndian.PutUint32(b, e)
  fmt.Printf("b: BigEndian.PutUint32 %x (Bad) %s\n", b, base64Encode(b))

  b2 := make([]byte, 4)
  binary.BigEndian.PutUint32(b2, e)
  for i := range b2 {
    if b2[i] != 0 {
    b2 = b2[i:]
    break
     }
  }
  fmt.Printf("b2: BigEndian.PutUint32 %x (Good) %s\n", b2, base64Encode(b2))

  b4 := big.NewInt(int64(e)).Bytes()
  fmt.Printf("b4: big.NewInt(int64(e)).Bytes() %x (Good) %s\n", b4, base64Encode(b4))
}

输出:

exp 65537
b: BigEndian.PutUint32 00010001 (Bad) AAEAAQ
b2: BigEndian.PutUint32 010001 (Good) AQAB
b4: big.NewInt(int64(e)).Bytes() 010001 (Good) AQAB

exp 1
b: BigEndian.PutUint32 00000001 (Bad) AAAAAQ
b2: BigEndian.PutUint32 01 (Good) AQ
b4: big.NewInt(int64(e)).Bytes() 01 (Good) AQ

exp 1000000
b: BigEndian.PutUint32 000f4240 (Bad) AA9CQA
b2: BigEndian.PutUint32 0f4240 (Good) D0JA
b4: big.NewInt(int64(e)).Bytes() 0f4240 (Good) D0JA

编辑:

我已经对b2和b4进行了基准测试:

b2  1000000000          68.1 ns/op         8 B/op          1 allocs/op
b4  200000000          248 ns/op          90 B/op          3 allocs/op

我暂时会使用b2...

2个回答

5
对于这种任务,我认为你的第一选择应该始终是使用encoding/binary,如果不够,则使用位运算。然而,在某些情况下,复制数据的开销太大或这些“安全”解决方案太慢:
虽然我不认为它很优雅,但你可以使用Go的unsafereflect*包来快速完成此操作。只需要记住,这不会复制数据;相反,它只是给您另一个“视图”。由于它不够安全,所以需要非常小心-(有单元测试和代码审查),并牢记您正在破坏Go的内存安全。然而,当执行速度是主要问题,且您的团队同意使用unsafe时,unsafe很少会被击败。
const BYTES_IN_INT32 = 4

func UnsafeCaseInt32ToBytes(val int32) []byte {
    hdr := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(&val)), Len: BYTES_IN_INT32, Cap: BYTES_IN_INT32}
    return *(*[]byte)(unsafe.Pointer(&hdr))
}

func UnsafeCastInt32sToBytes(ints []int32) []byte {        
    length := len(ints) * BYTES_IN_INT32
    hdr := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(&ints[0])), Len: length, Cap: length}
    return *(*[]byte)(unsafe.Pointer(&hdr))
}

*注意:您可能希望使用SizeOf而不是常量。我更喜欢使用常量。
更新:这里是一些基准测试结果:
BenchmarkB2     20000000     88.7  ns/op
BenchmarkB4     5000000     309    ns/op
BenchmarkUnsafe 1000000000    2.25 ns/op

5
unsafe 不仅会破坏内存安全性,还会引入字节序问题。 - Fred Foo
7
不,移位运算是安全的。x >> 24 可以获取 x 的高8位,无论字节序如何。 - Fred Foo
4
使用适当的掩码从32位数中分离出一个字节与字节序无关。此外,OP的问题并不涉及[]int32 -> []byte,而是int32 -> []byte,因此我猜你的答案无效。(-1) - zzzz
5
一个可怕且不必要的解决方案。 - peterSO
2
@peterSO,你怎么能在不知道用户使用情况的情况下就这么说呢?虽然应该避免使用unsafe,但这并不意味着它永远无效。我并没有省略使用它时通常涉及的警告。 - voidlogic
显示剩余2条评论

-1
这是最直接(也是最短(最安全)(可能是最高效))的方法: buf.Bytes() 是一个字节切片类型。
    var val uint32 = 42
    buf := new(bytes.Buffer)
    err := binary.Write(buf, binary.LittleEndian, val)
    if err != nil {
        fmt.Println("binary.Write failed:", err)
    }
    fmt.Printf("% x\n", buf.Bytes())

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