在Python中读取预先格式化的二进制文件

时间:2016-08-19 00:57:39

标签: python matlab file numpy binary

因此,我使用MATLAB创建了一个简单的二进制文件,其结构如下:

file.test
--------
[record_type] = 1 % 'int', 4 bytes, record_type = 1 means a string is read next
[string_length] = len(str) % 'int', 4 bytes, tells us how many bytes to read
[string] = '...' % 'char', the number of bytes in string length

[record_type] = 2 % 'int', 4 bytes, record_type = 2 means a vector will be read
[rows] = size(vector,1) % 'int', 4 bytes
[columns] = size(vector,2) % 'int', 4 bytes
[vector] = (vector) % 'double'

我可以使用以下代码在MATLAB中读取此文件(这只是代码的读取部分,我确实有错误检查和其他内容):

fid=fopen('file.test','rb')
record_names={}
record_data=[]

while ~feof(fid)
    [record_name_type,count]=fread(fid,1,'int');
    if count == 0 % reached eof
        break;
    end
    record_name_length=fread(fid,1,'int');
    record_names{end+1}=char(fread(fid,record_name_length,'char')');
    % read vector
    record_type=fread(fid,1,'int');
    rows=fread(fid,1,'int');
    cols=fread(fid,1,'int');
    record_data{end+1}=fread(fid,[rows,cols],'double');
end

所以,我必须将同样的功能翻译成Python 2.7。不幸的是,它被证明是非常困难的。我不能再调用fread并告诉它我想读多少元素或字节。我使用structs尝试了以下代码:

def read_file(filename):
    # checking to see if file exists / other checks happen
    fid=open(filename,'rb')
    record_data=[]
    record_names=[]
    result=read_record(fid)
    while result:
        record_names.append(result['record_name'])
        record_names.append(result['data'])
        result=read_record(fid)
    fid.close()
    return (record_names, record_data)

def read_record(fid):
    try:
        # read name
        record_name_type = struct.unpack('i',fid.read(struct.calcsize('i')))[0]
        record_name_len = struct.unpack('i',fid.read(struct.calcsize('i')))[0]
        record_name = struct.unpack('s',fid.read(record_name_len))[0]
        # read vector
        record_type = struct.unpack('i',fid.read(struct.calcsize('i')))[0]
        rows = struct.unpack('i',fid.read(struct.calcsize('i')))[0]
        cols = struct.unpack('i',fid.read(struct.calcsize('i')))[0]
        record_data = numpy.array(struct.unpack('%dd' % rows,fid.read(rows*struct.calcsize('d'))),dtype=float)
        # store into result and return
        result=OrderedDict()
        result['record_name']=record_name
        result['data']=record_data
    except struct.error as e:
        print e
        result=None
    return result

现在在python中运行我的方法时,我收到以下错误:

unpack requires a string argument of length 1来自except strcut.error as e部分。我有一种感觉,我正在错误地读取矢量,但我不知道我是如何错误地读取字符串的。

有谁知道更简单的方法来阅读这个二进制文件?或者是否有一些我可以遵循的教程来帮助我理解如何在Python中正确使用结构?我真的很陌生,尤其是Python中的这个领域。

1 个答案:

答案 0 :(得分:0)

您可以使用Construct

from construct import *

MyData = Struct(
    "MyData",
    SBInt32("record_type"),
    Switch("value", lambda ctx: ctx.record_type,
    {
        1: Embed(Struct(
            'string_data',
            SBInt32("string_length"), Bytes("string", lambda ctx: ctx.string_length))),
        2: Embed(Struct(
            'vector_data',
            SBInt32("rows"), SBInt32("columns"),
            Array(lambda ctx: ctx.rows * ctx.columns, LFloat64("vector"))))
    }
    )
)

s = MyData.build(Container(record_type=1, string_length=4, string='abcd'))
print(repr(s))
print(MyData.parse(s))

s = MyData.build(Container(record_type=2, rows=2, columns=3, vector=[1, 2, 3, 4, 5, 6]))
print(repr(s))
print(MyData.parse(s))

输出:

'\x00\x00\x00\x01\x00\x00\x00\x04abcd'
Container:
    record_type = 1
    string_length = 4
    string = 'abcd'
'\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x08@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@'
Container:
    record_type = 2
    rows = 2
    columns = 3
    vector = [
        1.0
        2.0
        3.0
        4.0
        5.0
        6.0
    ]