【问题标题】:Read/Write Python List from/to Binary file从/向二进制文件读/写 Python 列表
【发布时间】:2017-04-12 20:59:40
【问题描述】:

根据 Python Cookbook,以下是如何将元组列表写入二进制文件:

from struct import Struct
def write_records(records, format, f):
    '''
    Write a sequence of tuples to a binary file of structures.
    '''
    record_struct = Struct(format)
    for r in records:
        f.write(record_struct.pack(*r))

# Example
if __name__ == '__main__':
    records = [ (1, 2.3, 4.5),
                (6, 7.8, 9.0),
                (12, 13.4, 56.7) ]
    with open('data.b', 'wb') as f:
        write_records(records, '<idd', f)

而且效果很好。 对于读取(大量二进制数据),作者推荐如下:

>>> import numpy as np
>>> f = open('data.b', 'rb')
>>> records = np.fromfile(f, dtype='<i,<d,<d')
>>> records
array([(1, 2.3, 4.5), (6, 7.8, 9.0), (12, 13.4, 56.7)],
dtype=[('f0', '<i4'), ('f1', '<f8'), ('f2', '<f8')])
>>> records[0]
(1, 2.3, 4.5)
>>> records[1]
(6, 7.8, 9.0)
>>>

也不错,不过这个record不是普通的numpy数组。例如,type(record[0]) 将返回 &lt;type 'numpy.void'&gt;。更糟糕的是,我无法使用X = record[:, 0] 提取第一列。

有没有办法有效地将二进制文件中的列表(或任何其他类型)加载到普通的 numpy 数组中? 提前谢谢。

【问题讨论】:

    标签: python arrays numpy binaryfiles


    【解决方案1】:
    In [196]: rec = np.fromfile('data.b', dtype='<i,<d,<d')
    In [198]: rec
    Out[198]: 
    array([( 1,   2.3,   4.5), ( 6,   7.8,   9. ), (12,  13.4,  56.7)], 
          dtype=[('f0', '<i4'), ('f1', '<f8'), ('f2', '<f8')])
    

    这是一个一维结构化数组

    In [199]: rec['f0']
    Out[199]: array([ 1,  6, 12], dtype=int32)
    In [200]: rec.shape
    Out[200]: (3,)
    In [201]: rec.dtype
    Out[201]: dtype([('f0', '<i4'), ('f1', '<f8'), ('f2', '<f8')])
    

    请注意,它的 tolist 看起来与您原来的 records 相同:

    In [202]: rec.tolist()
    Out[202]: [(1, 2.3, 4.5), (6, 7.8, 9.0), (12, 13.4, 56.7)]
    In [203]: records
    Out[203]: [(1, 2.3, 4.5), (6, 7.8, 9.0), (12, 13.4, 56.7)]
    

    您可以使用以下任一列表创建二维数组:

    In [204]: arr2 = np.array(rec.tolist())
    In [205]: arr2
    Out[205]: 
    array([[  1. ,   2.3,   4.5],
           [  6. ,   7.8,   9. ],
           [ 12. ,  13.4,  56.7]])
    In [206]: arr2.shape
    Out[206]: (3, 3)
    

    还有其他方法可以将结构化数组转换为“常规”数组,但这是最简单且最一致的。

    常规数组的tolist 使用嵌套列表。结构化版本中的元组旨在传达差异:

    In [207]: arr2.tolist()
    Out[207]: [[1.0, 2.3, 4.5], [6.0, 7.8, 9.0], [12.0, 13.4, 56.7]]
    

    在结构化数组中,第一个字段是整数。在常规数组中,第一列与其他列相同,浮动。

    如果二进制文件包含所有浮点数,您可以将其加载为 1d 的浮点数并重新整形

    In [208]: with open('data.f', 'wb') as f:
         ...:         write_records(records, 'ddd', f)
    In [210]: rec2 = np.fromfile('data.f', dtype='<d')
    In [211]: rec2
    Out[211]: array([  1. ,   2.3,   4.5,   6. ,   7.8,   9. ,  12. ,  13.4,  56.7])
    

    但是要利用二进制文件中的任何记录结构,您也可以按记录加载,这意味着结构化数组:

    In [213]: rec3 = np.fromfile('data.f', dtype='d,d,d')
    In [214]: rec3
    Out[214]: 
    array([(  1.,   2.3,   4.5), (  6.,   7.8,   9. ), ( 12.,  13.4,  56.7)], 
          dtype=[('f0', '<f8'), ('f1', '<f8'), ('f2', '<f8')])
    

    【讨论】:

    • 非常感谢@hpaulj 你的回答真的很棒!但是我还有一个问题:假设所有 9 个数据都是 float64,有什么方法可以直接将它们加载到 3×3 numpy 数组中,而不需要转换两次?
    • 正如fromfile 演示的那样,创建一个具有指定数据类型的一维数组。你可以给它一个计数,但没有形状。但是一旦你有一个 (9,) 数组,将它重塑为 (3,3) 是微不足道的。 np.save/load 对使用标头来记录 dtype 和 shape,fromfile 只是读取数据字节。您提供数据类型和形状。
    猜你喜欢
    • 2016-08-30
    • 1970-01-01
    • 2012-07-12
    • 1970-01-01
    • 1970-01-01
    • 2015-05-15
    • 2012-01-26
    • 2018-07-26
    • 2016-06-14
    相关资源
    最近更新 更多