我有一个接收
[]byte
的函数,但我拥有的是 int
,进行此转换的最佳方法是什么?
err = a.Write([]byte(myInt))
我想我可以走很长的路,把它变成一个字符串,然后把它放入字节中,但这听起来很难看,我想有更好的方法来做到这一点。
我同意 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)
}
抱歉,这可能有点晚了。但我认为我在 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())
我认为 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
但是,该方法使用绝对值。 如果多花1个字节,就可以转移符号
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
或新的(https://play.golang.org/p/7ZAK4QL96FO)
(该包还提供了填充现有切片的功能)
添加此选项来处理基本的 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
这是另一个选项,基于 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
将整数转换为字节切片。
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()
}
也许简单的方法是使用 protobuf,请参阅 Protocol Buffer Basics: Go
定义消息如
message MyData {
int32 id = 1;
}
了解更多信息定义协议格式
// Write
out, err := proto.Marshal(mydata)
阅读更多内容写消息
尝试使用 math/big 包将 bytes 数组转换为 int 并将 int 转换为 bytes 数组。
package main
import (
"fmt"
"math/big"
)
func main() {
// Convert int to []byte
var int_to_encode int64 = 65535
var bytes_array []byte = big.NewInt(int_to_encode).Bytes()
fmt.Println("bytes array", bytes_array)
// Convert []byte to int
var decoded_int int64 = new(big.Int).SetBytes(bytes_array).Int64()
fmt.Println("decoded int", decoded_int)
}
这是最直接的(也是最短的(也是最安全的)(也许是最高效的))方式:
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())
步骤1.忽略字节序,对任意类型直接执行:
func toBytes[T any](val T) []byte {
return unsafe.Slice((*byte)(unsafe.Pointer(&val)), unsafe.Sizeof(val))
}
(还可以考虑在类型 T 上添加 整数约束)
第 2 步:您真的需要不同字节序机器之间的可移植性吗?好的,这是一个想法:
func toLEBytes[T any](val T) []byte {
bytes := toBytes(val)
if runninOnBigEndian() {
slices.Reverse(bytes)
}
return bytes
}
您唯一需要的就是实施
runninOnBigEndian()
。首先阅读这个问题的答案:
在 Go 中检查字节序的更好方法
转换为字符串有什么问题吗?
[]byte(fmt.Sprintf("%d", myint))