为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕桂英546537 2021-10-11 13:37:15
在最近的一个 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=utf8import structif __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 mainimport (        "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: 3After 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 生成的文件(带填充)?
查看完整描述

3 回答

?
慕妹3242003

TA贡献1824条经验 获得超6个赞

例如,


package main


import (

    "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                           |........|


查看完整回答
反对 回复 2021-10-11
?
料青山看我应如是

TA贡献1772条经验 获得超8个赞

我发现添加填充字段不太舒服。我找到了一个更好的方法。


下面是完美运行的新 golang 读取代码:


package main


import (

    "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。


查看完整回答
反对 回复 2021-10-11
  • 3 回答
  • 0 关注
  • 199 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信