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

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

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

问题

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

type transProp struct {
    a []int
    b []float64
}

func (p transProp) MarshalBinary() ([]byte, error) {
    // 简单的编码:纯文本。
    var b bytes.Buffer
    fmt.Fprintln(&b, p.a, p.b)
    return b.Bytes(), nil
}

// UnmarshalBinary 修改接收器,因此必须使用指针接收器。
func (p *transProp) UnmarshalBinary(data []byte) error {
    // 简单的编码:纯文本。
    b := bytes.NewBuffer(data)
    _, err := fmt.Fscanln(b, &p.a, &p.b)
    return err
}

func TestGobEncode(t *testing.T) {
    p := transProp{
        a: []int{3, 4, 5},
        b: []float64{1.0, 2.0},
    }

    var network bytes.Buffer
    enc := gob.NewEncoder(&network)
    err := enc.Encode(p)
    if err != nil {
        log.Fatal("encode:", err)
    }
    dec := gob.NewDecoder(&network)
    var p1 transProp
    err = dec.Decode(&p1)
    if err != nil {
        log.Fatal("decode:", err)
    }
    fmt.Println(p1)
}

但是,报告了以下错误:

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

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

type transProp struct {
	a []int
	b []float64
}

func (p transProp) MarshalBinary() ([]byte, error) {
	// A simple encoding: plain text.
	var b bytes.Buffer
	fmt.Fprintln(&b, p.a, p.b)
	return b.Bytes(), nil
}

// UnmarshalBinary modifies the receiver so it must take a pointer receiver.
func (p *transProp) UnmarshalBinary(data []byte) error {
	// A simple encoding: plain text.
	b := bytes.NewBuffer(data)
	_, err := fmt.Fscanln(b, &p.a, &p.b)
	return err
}

func TestGobEncode(t *testing.T) {
	p := transProp{
		a: []int{3, 4, 5},
		b: []float64{1.0, 2.0},
	}

	var network bytes.Buffer
	enc := gob.NewEncoder(&network)
	err := enc.Encode(p)
	if err != nil {
		log.Fatal("encode:", err)
	}
	dec := gob.NewDecoder(&network)
	var p1 transProp
	err = dec.Decode(&p1)
	if err != nil {
		log.Fatal("decode:", err)
	}
	fmt.Println(p1)
}

But, this report error like this:

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

答案1

得分: 8

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

type transProp struct {
    A []int
    B []float64
}

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

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

  1. 最简单的方法是定义另一个导出相关字段的包装结构体,将transProp包装起来,然后使用默认的gob编码器/解码器,例如:
type wrapTransProp struct {
    A []int
    B []float64
}

func (p transProp) MarshalBinary() ([]byte, error) {
    // 包装结构体
    w := wrapTransProp{p.A, p.B}

    // 使用默认的gob编码器
    var buf bytes.Buffer
    enc := gob.NewEncoder(&buf)
    if err := enc.Encode(w); err != nil {
        return nil, err
    }
    return buf.Bytes(), nil
}

func (p *transProp) UnmarshalBinary(data []byte) error {
    w := wrapTransProp{}

    // 使用默认的gob解码器
    reader := bytes.NewReader(data)
    dec := gob.NewDecoder(reader)
    if err := dec.Decode(&w); err != nil {
        return err
    }

    p.A = w.A
    p.B = w.B
    return nil
}
  1. 使用自定义的编码器/解码器和自定义的数据布局。有很多实现可能性。以下是一些考虑因素:
    • 字节顺序,小端/大端?
    • 数据包/流的布局?

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

// 大端字节顺序
// 大小:4,         4,      1,        n,     4,       n
// 类型:uint32,    uint32, uint8,    []int, uint32,  []float64
// 数据:#numbytes, #nInt,  #intSize, p.A,   #nFloat, p.B

挑战在于如何表示`int`因为它取决于架构以下是一个示例实现

func (p transProp) MarshalBinary() ([]byte, error) {
    // 从strconv包获取int的大小(以位为单位)
    szInt := strconv.IntSize / 8
    nInt := len(p.A)
    nFloat := len(p.B)

    nStream := 4 + 4 + 1 + nInt*szInt + 4 + nFloat*8
    stream := make([]byte, nStream)
    pos := 0

    // 总字节数
    binary.BigEndian.PutUint32(stream, uint32(nStream))
    pos += 4

    // p.A中的项目数
    binary.BigEndian.PutUint32(stream[pos:], uint32(nInt))
    pos += 4

    // int大小
    stream[pos] = uint8(szInt)
    pos++

    // p.A中的项目
    switch szInt {
    case 1:
        for _, v := range p.A {
            stream[pos] = uint8(v)
            pos++
        }
    case 2: // 16位
        for _, v := range p.A {
            binary.BigEndian.PutUint16(stream[pos:], uint16(v))
            pos += 2
        }
    case 4: // 32位
        for _, v := range p.A {
            binary.BigEndian.PutUint32(stream[pos:], uint32(v))
            pos += 4
        }
    case 8: // 64位
        for _, v := range p.A {
            binary.BigEndian.PutUint64(stream[pos:], uint64(v))
            pos += 8
        }
    }

    // p.B中的项目数
    binary.BigEndian.PutUint32(stream[pos:], uint32(nFloat))
    pos += 4

    // p.B中的项目
    s := stream[pos:pos] // 切片长度为0,容量为nFloat
    buf := bytes.NewBuffer(s)
    if err := binary.Write(buf, binary.BigEndian, p.B); err != nil {
        return nil, err
    }

    return stream, nil
}

func (p *transProp) UnmarshalBinary(data []byte) error {
    buf := bytes.NewBuffer(data)

    var intSize uint8
    var k, nBytes, nInt, nFloat uint32

    // 字节数
    if err := binary.Read(buf, binary.BigEndian, &nBytes); err != nil {
        return err
    }
    if len(data) < int(nBytes) {
        return errors.New("len(data) < #Bytes")
    }

    // int项目数
    if err := binary.Read(buf, binary.BigEndian, &nInt); err != nil {
        return err
    }

    // int大小
    if err := binary.Read(buf, binary.BigEndian, &intSize); err != nil {
        return err
    }

    // 读取int到p.A
    pos := 0
    stream := buf.Bytes()
    p.A = make([]int, nInt)
    switch intSize {
    case 1:
        for pos = 0; pos < int(nInt); pos++ {
            p.A[pos] = int(stream[pos])
        }
    case 2:
        for k = 0; k < nInt; k++ {
            p.A[k] = int(binary.BigEndian.Uint16(stream[pos:]))
            pos += 2
        }
    case 4:
        for k = 0; k < nInt; k++ {
            p.A[k] = int(binary.BigEndian.Uint32(stream[pos:]))
            pos += 4
        }
    case 8:
        for k = 0; k < nInt; k++ {
            p.A[k] = int(binary.BigEndian.Uint64(stream[pos:]))
            pos += 8
        }
    }

    // 推进缓冲区
    buf.Next(pos)

    // float64项目数
    if err := binary.Read(buf, binary.BigEndian, &nFloat); err != nil {
        return err
    }

    // p.B中的项目
    p.B = make([]float64, nFloat)
    if err := binary.Read(buf, binary.BigEndian, p.B); err != nil {
        return err
    }

    return nil
}

希望这可以帮助到您!

英文:

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

type transProp struct {
A []int
B []float64
}

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.

    type wrapTransProp struct {
    A []int
    B []float64
    }
    func (p transProp) MarshalBinary() ([]byte, error) {
    //Wrap struct
    w := wrapTransProp{p.a, p.b}
    //use default gob encoder
    var buf bytes.Buffer
    enc := gob.NewEncoder(&amp;buf)
    if err := enc.Encode(w); err != nil {
    return nil, err
    }
    return buf.Bytes(), nil
    }
    func (p *transProp) UnmarshalBinary(data []byte) error {
    w := wrapTransProp{}
    //Use default gob decoder
    reader := bytes.NewReader(data)
    dec := gob.NewDecoder(reader)
    if err := dec.Decode(&amp;w); err != nil {
    return err
    }
    p.a = w.A
    p.b = w.B
    return nil
    }
    
  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:

    // Big-Endian
// Size  : 4,         4,      1,        n,     4,       n
// Types : uint32,    uint32, uint8,    []int, uint32,  []float64
// 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:

    func (p transProp) MarshalBinary() ([]byte, error) {
//Get sizeof int (in bits) from strconv package
szInt := strconv.IntSize / 8
nInt := len(p.a)
nFloat := len(p.b)
nStream := 4 + 4 + 1 + nInt*szInt + 4 + nFloat*8
stream := make([]byte, nStream)
pos := 0
//total number of bytes
binary.BigEndian.PutUint32(stream, uint32(nStream))
pos += 4
//num of items in p.a
binary.BigEndian.PutUint32(stream[pos:], uint32(nInt))
pos += 4
//int size
stream[pos] = uint8(szInt)
pos++
//items in a
switch szInt {
case 1:
for _, v := range p.a {
stream[pos] = uint8(v)
pos++
}
case 2: //16-bit
for _, v := range p.a {
binary.BigEndian.PutUint16(stream[pos:], uint16(v))
pos += 2
}
case 4: //32-bit
for _, v := range p.a {
binary.BigEndian.PutUint32(stream[pos:], uint32(v))
pos += 4
}
case 8: //64-bit
for _, v := range p.a {
binary.BigEndian.PutUint64(stream[pos:], uint64(v))
pos += 8
}
}
//number of items in p.b
binary.BigEndian.PutUint32(stream[pos:], uint32(nFloat))
pos += 4
//items in b
s := stream[pos:pos] //slice len=0, capacity=nFloat
buf := bytes.NewBuffer(s)
if err := binary.Write(buf, binary.BigEndian, p.b); err != nil {
return nil, err
}
return stream, nil
}
func (p *transProp) UnmarshalBinary(data []byte) error {
buf := bytes.NewBuffer(data)
var intSize uint8
var k, nBytes, nInt, nFloat uint32
//num bytes
if err := binary.Read(buf, binary.BigEndian, &amp;nBytes); err != nil {
return err
}
if len(data) &lt; int(nBytes) {
return errors.New(&quot;len(data) &lt; #Bytes&quot;)
}
//num of int items
if err := binary.Read(buf, binary.BigEndian, &amp;nInt); err != nil {
return err
}
//int size
if err := binary.Read(buf, binary.BigEndian, &amp;intSize); err != nil {
return err
}
//read int into p.a
pos := 0
stream := buf.Bytes()
p.a = make([]int, nInt)
switch intSize {
case 1:
for pos = 0; pos &lt; int(nInt); pos++ {
p.a[pos] = int(stream[pos])
}
case 2:
for k = 0; k &lt; nInt; k++ {
p.a[k] = int(binary.BigEndian.Uint16(stream[pos:]))
pos += 2
}
case 4:
for k = 0; k &lt; nInt; k++ {
p.a[k] = int(binary.BigEndian.Uint32(stream[pos:]))
pos += 4
}
case 8:
for k = 0; k &lt; nInt; k++ {
p.a[k] = int(binary.BigEndian.Uint64(stream[pos:]))
pos += 8
}
}
//advance buffer
buf.Next(pos)
//num of float64 items
if err := binary.Read(buf, binary.BigEndian, &amp;nFloat); err != nil {
return err
}
//items in b
p.b = make([]float64, nFloat)
if err := binary.Read(buf, binary.BigEndian, p.b); err != nil {
return err
}
return nil
}

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:

确定