2017-05-31 204 views
0

我想用gobencodedecode對象,我不喜歡這樣寫道:如何將數組編碼爲二進制並將二進制數據解組到二進制數組中?

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 
+1

的println/Scanln並不意味着用於序列化。你可以實現'fmt.Scanner',但是你可以直接在你的Marshal方法中執行。 – JimB

回答

0

如果你可以改變transProp領域的知名度公開,例如

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

那麼你不需要實現自定義二進制編組/解組器。您可以使用默認的gob解碼器/編碼器。

但是,如果你不能,那麼有很多選擇。

  1. 最簡單的一種,定義另一個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 
    } 
    
  2. 自定義編組/解組自定義數據佈局。有很多實現的可能性。幾個注意事項:

    • 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 
    }