如何将数组编码为二进制并将二进制数据解组到二进制数组中?
问题描述:
我想用gob
到encode
和decode
对象,我不喜欢这样写道:如何将数组编码为二进制并将二进制数据解组到二进制数组中?
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)
}
不过,这份报告的错误是这样的:
decode:can't scan type: *[]int
答
如果你可以改变transProp
领域的知名度公开,例如
type transProp struct {
A []int
B []float64
}
那么你不需要实现自定义二进制编组/解组器。您可以使用默认的gob
解码器/编码器。
但是,如果你不能,那么有很多选择。
-
最简单的一种,定义另一个
wrapper struct
该导出相关领域,包transProp
,然后使用默认gob
编码器/解码器,例如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(&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(&w); err != nil { return err } p.a = w.A p.b = w.B return nil }
-
自定义编组/解组自定义数据布局。有很多实现的可能性。几个注意事项:
- Byte order,little/big endian?
- 数据包/流布局?
一个例子实施
big-endian
与流格式:// 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
的挑战是如何表示
int
,因为它的体系结构相关。样品实施将是: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, &nBytes); err != nil { return err } if len(data) < int(nBytes) { return errors.New("len(data) < #Bytes") } //num of int items if err := binary.Read(buf, binary.BigEndian, &nInt); err != nil { return err } //int size if err := binary.Read(buf, binary.BigEndian, &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 < 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 } } //advance buffer buf.Next(pos) //num of float64 items if err := binary.Read(buf, binary.BigEndian, &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 }
的println/Scanln并不意味着用于序列化。你可以实现'fmt.Scanner',但是你可以直接在你的Marshal方法中执行。 – JimB