猿问

binary.Read 未按预期处理结构填充

在最近的一个 Go 项目中,我需要读取一个由 Python 生成的二进制数据文件,但由于填充,binary.Read在 Go 中无法正确读取它。以下是我的问题的最小示例。


我处理的结构是否具有以下格式


type Index struct{

    A int32

    B int32

    C int32

    D int64

}

如您所见,结构体的大小为 4+4+4+8=20,但 Python 额外添加了 4 个字节用于对齐。所以大小实际上是24。


下面是我用来编写这个结构体的可运行 Python 代码:


#!/usr/bin/env python

# encoding=utf8


import struct


if __name__ == '__main__':

    data = range(1, 13)

    format = 'iiiq' * 3

    content = struct.pack(format, *data)

    with open('index.bin', 'wb') as f:

        f.write(content)

的iiiq格式的装置有三个32位的整数,并且在结构中,其是与相同的一个64位的整数Index余前面定义结构。运行此代码将生成一个名为index.bin72的文件,它等于 24 * 3。


下面是我用来阅读的 Go 代码index.bin:


package main


import (

        "encoding/binary"

        "fmt"

        "os"

        "io"

        "unsafe"

)


type Index struct {

        A int32

        B int32

        C int32

        D int64

}


func main() {

        indexSize := unsafe.Sizeof(Index{})

        fp, _ := os.Open("index.bin")

        defer fp.Close()

        info, _ := fp.Stat()

        fileSize := info.Size()

        entryCnt := fileSize / int64(indexSize)

        fmt.Printf("entry cnt: %d\n", entryCnt)


        readSlice := make([]Index, entryCnt)

        reader := io.Reader(fp)

        _ = binary.Read(reader, binary.LittleEndian, &readSlice)

        fmt.Printf("After read:\n%#v\n", readSlice)

}

这是输出:


entry cnt: 3

After read:

[]main.Index{main.Index{A:1, B:2, C:3, D:17179869184}, main.Index{A:0, B:5, C:6, D:7}, main.Index{A:8, B:0, C:9, D:47244640266}}

显然,从 Python 生成的文件中读取时,输出是混乱的。


所以我的问题是,如何在 Go 中正确读取 Python 生成的文件(带填充)?


慕桂英546537
浏览 200回答 3
3回答

慕妹3242003

例如,package mainimport (    "bufio"    "encoding/binary"    "fmt"    "io"    "os")type Index struct {    A int32    B int32    C int32    D int64}func readIndex(r io.Reader) (Index, error) {    var index Index    var buf [24]byte    _, err := io.ReadFull(r, buf[:])    if err != nil {        return index, err    }    index.A = int32(binary.LittleEndian.Uint32(buf[0:4]))    index.B = int32(binary.LittleEndian.Uint32(buf[4:8]))    index.C = int32(binary.LittleEndian.Uint32(buf[8:12]))    index.D = int64(binary.LittleEndian.Uint64(buf[16:24]))    return index, nil}func main() {    f, err := os.Open("index.bin")    if err != nil {        fmt.Fprintln(os.Stderr, err)        return    }    defer f.Close()    r := bufio.NewReader(f)    indexes := make([]Index, 0, 1024)    for {        index, err := readIndex(r)        if err != nil {            if err == io.EOF {                break            }            fmt.Fprintln(os.Stderr, err)            return        }        indexes = append(indexes, index)    }    fmt.Println(indexes)}输出:[{1 2 3 4} {5 6 7 8} {9 10 11 12}]输入:00000000  01 00 00 00 02 00 00 00  03 00 00 00 00 00 00 00  |................|00000010  04 00 00 00 00 00 00 00  05 00 00 00 06 00 00 00  |................|00000020  07 00 00 00 00 00 00 00  08 00 00 00 00 00 00 00  |................|00000030  09 00 00 00 0a 00 00 00  0b 00 00 00 00 00 00 00  |................|00000040  0c 00 00 00 00 00 00 00                           |........|

料青山看我应如是

我发现添加填充字段不太舒服。我找到了一个更好的方法。下面是完美运行的新 golang 读取代码:package mainimport (    "fmt"    "os"    "io"    "io/ioutil"    "unsafe")type Index struct {    A int32    B int32    C int32    // Pad int32    D int64}func main() {    indexSize := unsafe.Sizeof(Index{})    fp, _ := os.Open("index.bin")    defer fp.Close()    info, _ := fp.Stat()    fileSize := info.Size()    entryCnt := fileSize / int64(indexSize)    reader := io.Reader(fp)    allBytes, _ := ioutil.ReadAll(reader)    readSlice := *((*[]Index)(unsafe.Pointer(&allBytes)))    realLen := len(allBytes) / int(indexSize)    readSlice = readSlice[:realLen]    fmt.Printf("After read:\n%#v\n", readSlice)}输出:After read:[]main.Index{main.Index{A:1, B:2, C:3, D:4}, main.Index{A:5, B:6, C:7, D:8}, main.Index{A:9, B:10, C:11, D:12}}此解决方案不需要显式填充字段。这里的本质是,如果让golang将整个字节转换为Indexstruct的切片,它似乎能够很好地处理padding。
随时随地看视频慕课网APP

相关分类

Go
我要回答