我想将 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
}
那么,您无需实施自定义的二进制邮编/Unmarshaller。您可以使用默认的gob
解码器/编码器。
但是,如果不能,则有很多选择。
最简单的一个,定义另一个导出相关字段的
wrapper struct
,wraptransProp
,然后使用默认的gob
encoder/decoder,例如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 }
具有自定义数据布局的自定义邮政编码/Unmarshaller。实施可能性很多。几个考虑因素:
- 字节订单,小/大端?
- 数据包/流布局?
带有流格式的
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 }