How to marshal array to binary and unmarshal binary to array in golang?

huangapple go评论182阅读模式
英文:

How to marshal array to binary and unmarshal binary to array in golang?

问题

我想使用gob来对对象进行编码和解码,我是这样做的:

  1. type transProp struct {
  2. a []int
  3. b []float64
  4. }
  5. func (p transProp) MarshalBinary() ([]byte, error) {
  6. // 简单的编码:纯文本。
  7. var b bytes.Buffer
  8. fmt.Fprintln(&b, p.a, p.b)
  9. return b.Bytes(), nil
  10. }
  11. // UnmarshalBinary 修改接收器,因此必须使用指针接收器。
  12. func (p *transProp) UnmarshalBinary(data []byte) error {
  13. // 简单的编码:纯文本。
  14. b := bytes.NewBuffer(data)
  15. _, err := fmt.Fscanln(b, &p.a, &p.b)
  16. return err
  17. }
  18. func TestGobEncode(t *testing.T) {
  19. p := transProp{
  20. a: []int{3, 4, 5},
  21. b: []float64{1.0, 2.0},
  22. }
  23. var network bytes.Buffer
  24. enc := gob.NewEncoder(&network)
  25. err := enc.Encode(p)
  26. if err != nil {
  27. log.Fatal("encode:", err)
  28. }
  29. dec := gob.NewDecoder(&network)
  30. var p1 transProp
  31. err = dec.Decode(&p1)
  32. if err != nil {
  33. log.Fatal("decode:", err)
  34. }
  35. fmt.Println(p1)
  36. }

但是,报告了以下错误:

  1. decode:can't scan type: *[]int
英文:

I want to use gob to encode and decode object, I do it like this:

  1. type transProp struct {
  2. a []int
  3. b []float64
  4. }
  5. func (p transProp) MarshalBinary() ([]byte, error) {
  6. // A simple encoding: plain text.
  7. var b bytes.Buffer
  8. fmt.Fprintln(&b, p.a, p.b)
  9. return b.Bytes(), nil
  10. }
  11. // UnmarshalBinary modifies the receiver so it must take a pointer receiver.
  12. func (p *transProp) UnmarshalBinary(data []byte) error {
  13. // A simple encoding: plain text.
  14. b := bytes.NewBuffer(data)
  15. _, err := fmt.Fscanln(b, &p.a, &p.b)
  16. return err
  17. }
  18. func TestGobEncode(t *testing.T) {
  19. p := transProp{
  20. a: []int{3, 4, 5},
  21. b: []float64{1.0, 2.0},
  22. }
  23. var network bytes.Buffer
  24. enc := gob.NewEncoder(&network)
  25. err := enc.Encode(p)
  26. if err != nil {
  27. log.Fatal("encode:", err)
  28. }
  29. dec := gob.NewDecoder(&network)
  30. var p1 transProp
  31. err = dec.Decode(&p1)
  32. if err != nil {
  33. log.Fatal("decode:", err)
  34. }
  35. fmt.Println(p1)
  36. }

But, this report error like this:

  1. decode:can't scan type: *[]int

答案1

得分: 8

如果您可以将transProp字段的可见性更改为public,例如:

  1. type transProp struct {
  2. A []int
  3. B []float64
  4. }

那么您就不需要实现自定义的二进制编码/解码器。您可以使用默认的gob编码器/解码器。

然而,如果您不能更改可见性,有很多选项。

  1. 最简单的方法是定义另一个导出相关字段的包装结构体,将transProp包装起来,然后使用默认的gob编码器/解码器,例如:
  1. type wrapTransProp struct {
  2. A []int
  3. B []float64
  4. }
  5. func (p transProp) MarshalBinary() ([]byte, error) {
  6. // 包装结构体
  7. w := wrapTransProp{p.A, p.B}
  8. // 使用默认的gob编码器
  9. var buf bytes.Buffer
  10. enc := gob.NewEncoder(&buf)
  11. if err := enc.Encode(w); err != nil {
  12. return nil, err
  13. }
  14. return buf.Bytes(), nil
  15. }
  16. func (p *transProp) UnmarshalBinary(data []byte) error {
  17. w := wrapTransProp{}
  18. // 使用默认的gob解码器
  19. reader := bytes.NewReader(data)
  20. dec := gob.NewDecoder(reader)
  21. if err := dec.Decode(&w); err != nil {
  22. return err
  23. }
  24. p.A = w.A
  25. p.B = w.B
  26. return nil
  27. }
  1. 使用自定义的编码器/解码器和自定义的数据布局。有很多实现可能性。以下是一些考虑因素:
    • 字节顺序,小端/大端?
    • 数据包/流的布局?

以下是使用大端字节顺序和流格式的示例实现:

  1. // 大端字节顺序
  2. // 大小:4, 4, 1, n, 4, n
  3. // 类型:uint32, uint32, uint8, []int, uint32, []float64
  4. // 数据:#numbytes, #nInt, #intSize, p.A, #nFloat, p.B
  5. 挑战在于如何表示`int`因为它取决于架构以下是一个示例实现
  6. func (p transProp) MarshalBinary() ([]byte, error) {
  7. // 从strconv包获取int的大小(以位为单位)
  8. szInt := strconv.IntSize / 8
  9. nInt := len(p.A)
  10. nFloat := len(p.B)
  11. nStream := 4 + 4 + 1 + nInt*szInt + 4 + nFloat*8
  12. stream := make([]byte, nStream)
  13. pos := 0
  14. // 总字节数
  15. binary.BigEndian.PutUint32(stream, uint32(nStream))
  16. pos += 4
  17. // p.A中的项目数
  18. binary.BigEndian.PutUint32(stream[pos:], uint32(nInt))
  19. pos += 4
  20. // int大小
  21. stream[pos] = uint8(szInt)
  22. pos++
  23. // p.A中的项目
  24. switch szInt {
  25. case 1:
  26. for _, v := range p.A {
  27. stream[pos] = uint8(v)
  28. pos++
  29. }
  30. case 2: // 16位
  31. for _, v := range p.A {
  32. binary.BigEndian.PutUint16(stream[pos:], uint16(v))
  33. pos += 2
  34. }
  35. case 4: // 32位
  36. for _, v := range p.A {
  37. binary.BigEndian.PutUint32(stream[pos:], uint32(v))
  38. pos += 4
  39. }
  40. case 8: // 64位
  41. for _, v := range p.A {
  42. binary.BigEndian.PutUint64(stream[pos:], uint64(v))
  43. pos += 8
  44. }
  45. }
  46. // p.B中的项目数
  47. binary.BigEndian.PutUint32(stream[pos:], uint32(nFloat))
  48. pos += 4
  49. // p.B中的项目
  50. s := stream[pos:pos] // 切片长度为0,容量为nFloat
  51. buf := bytes.NewBuffer(s)
  52. if err := binary.Write(buf, binary.BigEndian, p.B); err != nil {
  53. return nil, err
  54. }
  55. return stream, nil
  56. }
  57. func (p *transProp) UnmarshalBinary(data []byte) error {
  58. buf := bytes.NewBuffer(data)
  59. var intSize uint8
  60. var k, nBytes, nInt, nFloat uint32
  61. // 字节数
  62. if err := binary.Read(buf, binary.BigEndian, &nBytes); err != nil {
  63. return err
  64. }
  65. if len(data) < int(nBytes) {
  66. return errors.New("len(data) < #Bytes")
  67. }
  68. // int项目数
  69. if err := binary.Read(buf, binary.BigEndian, &nInt); err != nil {
  70. return err
  71. }
  72. // int大小
  73. if err := binary.Read(buf, binary.BigEndian, &intSize); err != nil {
  74. return err
  75. }
  76. // 读取int到p.A
  77. pos := 0
  78. stream := buf.Bytes()
  79. p.A = make([]int, nInt)
  80. switch intSize {
  81. case 1:
  82. for pos = 0; pos < int(nInt); pos++ {
  83. p.A[pos] = int(stream[pos])
  84. }
  85. case 2:
  86. for k = 0; k < nInt; k++ {
  87. p.A[k] = int(binary.BigEndian.Uint16(stream[pos:]))
  88. pos += 2
  89. }
  90. case 4:
  91. for k = 0; k < nInt; k++ {
  92. p.A[k] = int(binary.BigEndian.Uint32(stream[pos:]))
  93. pos += 4
  94. }
  95. case 8:
  96. for k = 0; k < nInt; k++ {
  97. p.A[k] = int(binary.BigEndian.Uint64(stream[pos:]))
  98. pos += 8
  99. }
  100. }
  101. // 推进缓冲区
  102. buf.Next(pos)
  103. // float64项目数
  104. if err := binary.Read(buf, binary.BigEndian, &nFloat); err != nil {
  105. return err
  106. }
  107. // p.B中的项目
  108. p.B = make([]float64, nFloat)
  109. if err := binary.Read(buf, binary.BigEndian, p.B); err != nil {
  110. return err
  111. }
  112. return nil
  113. }

希望这可以帮助到您!

英文:

If you can change the visibility of transProp fields to public, e.g.

  1. type transProp struct {
  2. A []int
  3. B []float64
  4. }

then you don't need to implement custom binary marshaller/unmarshaller. You can use default gob decoder/encoder.

However, if you can't, there are many options.

  1. The simplest one, define another wrapper struct that export related fields, wrap transProp, then use default gob encoder/decoder, e.g.

    1. type wrapTransProp struct {
    2. A []int
    3. B []float64
    4. }
    5. func (p transProp) MarshalBinary() ([]byte, error) {
    6. //Wrap struct
    7. w := wrapTransProp{p.a, p.b}
    8. //use default gob encoder
    9. var buf bytes.Buffer
    10. enc := gob.NewEncoder(&amp;buf)
    11. if err := enc.Encode(w); err != nil {
    12. return nil, err
    13. }
    14. return buf.Bytes(), nil
    15. }
    16. func (p *transProp) UnmarshalBinary(data []byte) error {
    17. w := wrapTransProp{}
    18. //Use default gob decoder
    19. reader := bytes.NewReader(data)
    20. dec := gob.NewDecoder(reader)
    21. if err := dec.Decode(&amp;w); err != nil {
    22. return err
    23. }
    24. p.a = w.A
    25. p.b = w.B
    26. return nil
    27. }
  2. Custom marshaller/unmarshaller with custom data layout. There are many implementation possibilities. Several considerations:

  • Byte order, little/big endian?
  • Packet/stream layout?

An example implementation of big-endian with stream format:

  1. // Big-Endian
  2. // Size : 4, 4, 1, n, 4, n
  3. // Types : uint32, uint32, uint8, []int, uint32, []float64
  4. // Data : #numbytes, #nInt, #intSize, p.a, #nFloat, p.b

The challenge is in how to represent int since it's architecture dependent. Sample implementation will be:

  1. func (p transProp) MarshalBinary() ([]byte, error) {
  2. //Get sizeof int (in bits) from strconv package
  3. szInt := strconv.IntSize / 8
  4. nInt := len(p.a)
  5. nFloat := len(p.b)
  6. nStream := 4 + 4 + 1 + nInt*szInt + 4 + nFloat*8
  7. stream := make([]byte, nStream)
  8. pos := 0
  9. //total number of bytes
  10. binary.BigEndian.PutUint32(stream, uint32(nStream))
  11. pos += 4
  12. //num of items in p.a
  13. binary.BigEndian.PutUint32(stream[pos:], uint32(nInt))
  14. pos += 4
  15. //int size
  16. stream[pos] = uint8(szInt)
  17. pos++
  18. //items in a
  19. switch szInt {
  20. case 1:
  21. for _, v := range p.a {
  22. stream[pos] = uint8(v)
  23. pos++
  24. }
  25. case 2: //16-bit
  26. for _, v := range p.a {
  27. binary.BigEndian.PutUint16(stream[pos:], uint16(v))
  28. pos += 2
  29. }
  30. case 4: //32-bit
  31. for _, v := range p.a {
  32. binary.BigEndian.PutUint32(stream[pos:], uint32(v))
  33. pos += 4
  34. }
  35. case 8: //64-bit
  36. for _, v := range p.a {
  37. binary.BigEndian.PutUint64(stream[pos:], uint64(v))
  38. pos += 8
  39. }
  40. }
  41. //number of items in p.b
  42. binary.BigEndian.PutUint32(stream[pos:], uint32(nFloat))
  43. pos += 4
  44. //items in b
  45. s := stream[pos:pos] //slice len=0, capacity=nFloat
  46. buf := bytes.NewBuffer(s)
  47. if err := binary.Write(buf, binary.BigEndian, p.b); err != nil {
  48. return nil, err
  49. }
  50. return stream, nil
  51. }
  52. func (p *transProp) UnmarshalBinary(data []byte) error {
  53. buf := bytes.NewBuffer(data)
  54. var intSize uint8
  55. var k, nBytes, nInt, nFloat uint32
  56. //num bytes
  57. if err := binary.Read(buf, binary.BigEndian, &amp;nBytes); err != nil {
  58. return err
  59. }
  60. if len(data) &lt; int(nBytes) {
  61. return errors.New(&quot;len(data) &lt; #Bytes&quot;)
  62. }
  63. //num of int items
  64. if err := binary.Read(buf, binary.BigEndian, &amp;nInt); err != nil {
  65. return err
  66. }
  67. //int size
  68. if err := binary.Read(buf, binary.BigEndian, &amp;intSize); err != nil {
  69. return err
  70. }
  71. //read int into p.a
  72. pos := 0
  73. stream := buf.Bytes()
  74. p.a = make([]int, nInt)
  75. switch intSize {
  76. case 1:
  77. for pos = 0; pos &lt; int(nInt); pos++ {
  78. p.a[pos] = int(stream[pos])
  79. }
  80. case 2:
  81. for k = 0; k &lt; nInt; k++ {
  82. p.a[k] = int(binary.BigEndian.Uint16(stream[pos:]))
  83. pos += 2
  84. }
  85. case 4:
  86. for k = 0; k &lt; nInt; k++ {
  87. p.a[k] = int(binary.BigEndian.Uint32(stream[pos:]))
  88. pos += 4
  89. }
  90. case 8:
  91. for k = 0; k &lt; nInt; k++ {
  92. p.a[k] = int(binary.BigEndian.Uint64(stream[pos:]))
  93. pos += 8
  94. }
  95. }
  96. //advance buffer
  97. buf.Next(pos)
  98. //num of float64 items
  99. if err := binary.Read(buf, binary.BigEndian, &amp;nFloat); err != nil {
  100. return err
  101. }
  102. //items in b
  103. p.b = make([]float64, nFloat)
  104. if err := binary.Read(buf, binary.BigEndian, p.b); err != nil {
  105. return err
  106. }
  107. return nil
  108. }

huangapple
  • 本文由 发表于 2017年6月1日 00:56:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/44290639.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定