如何在Go中将字节转换为float32数组?

zf2sa74q  于 2024-01-04  发布在  Go
关注(0)|答案(1)|浏览(174)

我从Python脚本中以字节格式向Elasticache Redis集群中写入一个float32数组,然后在Go脚本中从Elasticache中阅读字节(作为字符串)。如何将字节作为字符串转换回Go脚本中原始的float32数组?
Python示例:

  1. import numpy as np
  2. import redis
  3. a = np.array([1.1, 2.2, 3.3], dtype=np.float32)
  4. a_bytes = a.tobytes(order="C") #I have also tried order="F" with no luck
  5. print(a_bytes) #Output: b'\xcd\xcc\x8c?\xcd\xcc\x0c@33S@'
  6. redis_client = redis.cluster.RedisCluster(host=<elasticache config endpoint>, port=6379)
  7. redis_client.mset_nonatomic({"key1": a_bytes})

字符串
下面是我在Go(playground)中尝试的一个例子。

  1. package main
  2. import (
  3. "fmt"
  4. "math"
  5. "strconv"
  6. )
  7. func main() {
  8. // aBytesStr is an example value retrieved from Elasticache
  9. // aBytesStr is type string, not raw bytes
  10. var aBytesStr string = "\xcd\xcc\x8c?\xcd\xcc\x0c@33S@"
  11. aHex := fmt.Sprintf("%X", aBytesStr)
  12. fmt.Println(aHex) // Output: CDCC8C3FCDCC0C4033335340
  13. var aArr [3]float32
  14. for i := 0; i < 3; i++ {
  15. aHex1 := aHex[i*8 : i*8+8]
  16. aParsed, err := strconv.ParseUint(aHex1, 16, 32)
  17. if err != nil {
  18. return
  19. }
  20. aArr[i] = math.Float32frombits(uint32(aParsed))
  21. }
  22. fmt.Println(aArr)
  23. // Expected output: [1.1 2.2 3.3]
  24. // Actual output [-4.289679e+08 -4.2791936e+08 4.17524e-08]
  25. }

jxct1oxe

jxct1oxe1#

你使用的示例代码是“转换十六进制,表示为字符串”;你有原始字节(我认为基于aHex: CDCC8C3FCDCC0C4033335340),所以直接转换更简单(虽然你可以将字节转换为十六进制字符串,然后转换,这样做只会增加不必要的工作/复杂性)。
this answer作图,我们得到(playground):

  1. func GetFloatArray(aBytes []byte) []float32 {
  2. aArr := make([]float32, 3)
  3. for i := 0; i < 3; i++ {
  4. aArr[i] = BytesFloat32(aBytes[i*4:])
  5. }
  6. return aArr
  7. }
  8. func BytesFloat32(bytes []byte) float32 {
  9. bits := binary.LittleEndian.Uint32(bytes)
  10. float := math.Float32frombits(bits)
  11. return float
  12. }

字符串
更新参考注解:
我将bytes-as-string转换为十六进制字符串,转换为[]bytes,转换为[] float 32。有没有一种方法可以直接将bytes-as-string转换为[]bytes?
我仍然有点困惑你收到什么,所以让我们通过这两种可能性的工作。
如果redis查询返回原始数据(字节)作为go字符串(即"\xcd\xcc\x8c?\xcd\xcc\x0c@33S@"),那么您可以将其转换为[]byteplayground

  1. func main() {
  2. var aBytesStr string = "\xcd\xcc\x8c?\xcd\xcc\x0c@33S@"
  3. fmt.Println(GetFloatArray([]byte(aBytesStr)))
  4. }


如果redis返回一个包含ASCII(/UTF-8)表示的字符串(即CDCC = []byte{0x41, 0x44, 0x43, 0x43}),最简单的方法可能是使用encoding/hex来解码(playground

  1. func main() {
  2. aHex := "CDCC8C3FCDCC0C4033335340"
  3. b, err := hex.DecodeString(aHex)
  4. if err != nil {
  5. panic(err)
  6. }
  7. fmt.Println(GetFloatArray(b))
  8. }


请注意,您原来的方法可以工作,但正如上面的评论中所指出的,您需要处理Endianness,以便以下工作(playground-您可以使其更有效,我的目的是为了清晰):

  1. byte1 := aHex[i*8 : i*8+2]
  2. byte2 := aHex[i*8+2 : i*8+4]
  3. byte3 := aHex[i*8+4 : i*8+6]
  4. byte4 := aHex[i*8+6 : i*8+8]
  5. aParsed, err := strconv.ParseUint(byte4+byte3+byte2+byte1, 16, 32)


然而,这对代码运行的CPU做出了假设,这意味着前面的答案更可取。

展开查看全部

相关问题