【问题标题】:Read binary data with header from C in python在python中从C中读取带有标头的二进制数据
【发布时间】:2016-04-01 02:13:19
【问题描述】:

我曾使用 C 编写二进制格式的文件。我使用的格式如下:

带有 5 个双精度的标头(共 40 个字节):

fwrite(&FirstNum, sizeof(double), 1, outFile);
fwrite(&SecNum, sizeof(double), 1, outFile);
fwrite(&ThirdNum, sizeof(double), 1, outFile);
fwrite(&FourthNum, sizeof(double), 1, outFile);           
fwrite(&FifthNum, sizeof(double), 1, outFile);

然后我对 256^3 个“粒子”执行了一个 for cicle。对于每个粒子,我写了 9 个值:第一个是整数,其他 8 个是双精度值,方式如下:

Ntot = 256*256*256
for(i=0; i<Ntot; i++ )
  {
    fwrite(&gp[i].GID, sizeof(int), 1, outFile);

    /*----- Positions -----*/
    pos_aux[X] = gp[i].pos[X];
    pos_aux[Y] = gp[i].pos[Y];
    pos_aux[Z] = gp[i].pos[Z];

    fwrite(&pos_aux[0], sizeof(double), 3, outFile);  //Positions in 3D
    fwrite(&gp[i].DenConCell, sizeof(double), 1, outFile); //Density
    fwrite(&gp[i].poten_r[0], sizeof(double), 1, outFile); //Field 1
    fwrite(&gp[i].potDot_r[0], sizeof(double), 1, outFile); //Field 2
    fwrite(&gp[i].potDot_app1[0], sizeof(double), 1, outFile); //Field 3
    fwrite(&gp[i].potDot_app2[0], sizeof(double), 1, outFile); //Field 4
  }

其中 gp 只是一个包含我的粒子信息的数据结构。然后,对于 256^3 个粒子中的每一个,我总共使用了 68 个字节:4 个字节用于 int + 8*(8 个字节) 用于双打。

我需要的是阅读这种格式,但在 python 中以便制作一些情节,但我对 python 有点陌生。我已经阅读了一些使用 python 以二进制格式读取文件的答案,但我只能读取我的标题,而不是“正文”或有关粒子的其余信息。我尝试过的如下:

Npart = 256
with open("./path/to/my/binary/file.bin", 'rb') as bdata:
    header_size = 40 # in bytes           
    bheader = bdata.read(40)
    header_data = struct.unpack('ddddd', bheader)
    FirstNum = header_data[0]
    SecNum = header_data[1]
    ThirdNum = header_data[2]
    FourthNum = header_data[3]
    FifthNum = header_data[4]
    #Until here, if I print each number, I obtain the correct values.
    #From here, is what I've tried in order to read the 9 data of the 
    #particles
    bytes_per_part = 68
    body_size = int( (Npart**3) * bytes_per_part )
    body_data_read = bdata.read(body_size)
    #body_data = struct.unpack_from('idddddddd', bdata, offset=40)
    #body_data = struct.unpack('=i 8d', body_data_read) 
    body_data = struct.unpack('<i 8d', body_data_read)

    #+++++ Unpacking data ++++++ 
    ID_us = body_data[0]
    pos_x_us = body_data[1]
    pos_y_us = body_data[2]
    pos_z_us = body_data[3]
    DenCon_us = body_data[4]

但是当我运行我的代码时,我得到了这个错误:

body_data = struct.unpack('<i 8d', body_data_read)
struct.error: unpack requires a string argument of length 68

我已经尝试过第一行注释:

#body_data = struct.unpack_from('idddddddd', bdata, offset=40)

但是错误提示:

struct.error: unpack requires a string argument of length 72

如果我使用这条线

    body_data = struct.unpack('=i 8d', body_data_read) 

或者一行

    body_data = struct.unpack('<i 8d', body_data_read)

我得到了我首先显示的错误:

struct.error: unpack requires a string argument of length 68

确实,我觉得我根本看不懂字符串字符“=”和“

【问题讨论】:

  • struct.unpack_from('='+(Npart**3)*'i8d', body_data_read) 有效吗?它应该一次读取所有数据,之后您可以将它们拆分为每 9 个值
  • 感谢您的回答。它看起来像工作,我没有同样的错误,但是当我尝试拆分它们时,就像我用pos_x_us = body_data[1] 展示的那样,它只分配一个数字,而不是 pos_x_us 的完整数组。我该怎么做?
  • 是的,body_data[1] 只会获取列表中第二个位置的值。如果你想要所有的 x 值,你想使用切片:body_data[1::9]
  • 成功了!非常感谢!

标签: python c binaryfiles


【解决方案1】:

您的问题出现是因为缓冲区大小与格式不匹配。让我们用一些随机数据来试试。总共 12 个字节,用于 int 和 float。

>>> data = '\xf4\x9f\x97\xcd\xf2\xbe\xd6\x87\x18\xe3\x17\xdf'

如果你不使用''、'='和'!',就会有padding

填充只会在连续的结构成员之间自动添加。编码结构的开头或结尾不添加任何填充。

>>> struct.unpack('id', data)

Traceback (most recent call last):
  File "<pyshell#56>", line 1, in <module>
    struct.unpack('id', data)
error: unpack requires a string argument of length 16

但是

>>> struct.unpack('=id', data)
(-845701132, -1.2217466572589222e+150)

更具体地说,“d”占用 8 个字节,“i”占用 4 个字节。“iii”单独占用 12 个字节,因为它是同一类型。但是如果你尝试做 'id',它不会喜欢这样,它会将整数填充到 8 个字节。你可以看到'c' 占用 1 个字节,但 'ci' 需要 8 个字节。基本上,struct.unpack('ddddd') 在某些情况下工作得很好。

您的其他错误来自与缓冲区大小不匹配的格式。如果你使用struct.unpack(),它必须完全匹配,但如果你使用struct.unpack_from(),你必须至少有格式的大小。让我们尝试使用 24 字节的数据。

# this will fetch 12 bytes, even if the stream has more
>>> struct.unpack_from('=id', 2*data)
(-845701132, -1.2217466572589222e+150)

但是

>>> struct.unpack('=id', 2*data)

Traceback (most recent call last):
  File "<pyshell#60>", line 1, in <module>
    struct.unpack('=id', 2*data)
error: unpack requires a string argument of length 12

如您现在所见,您的数据实际上是

body_size = int( (Npart**3) * bytes_per_part )
body_data_read = bdata.read(body_size)

为了匹配它,您需要 'i8di8di8d...' Npart**3 次的格式。所以,

body_data = struct.unpack('='+(Npart**3)*'i8d', body_data_read)

现在您已经一次读入了所有数据,您可以根据需要开始拆分它们。例如,第二个值具有第一个粒子的 x 坐标,由于此模式每 9 个值重复一次,因此您可以通过切片获得所有粒子的 x 坐标。

pos_x_us = body_data[1::9]

【讨论】:

  • 非常感谢,再次!你一直非常清楚和乐于助人。效果很好。
猜你喜欢
  • 2011-06-21
  • 1970-01-01
  • 2017-07-10
  • 2019-04-04
  • 2021-12-24
  • 2015-01-08
  • 2015-10-07
  • 2012-08-11
  • 2010-12-08
相关资源
最近更新 更多