繁体   English   中英

从 python 中的二进制文件中提取数据

[英]Extracting data from binary file in python

我有一个仪器(来自 Actimetrics 的 LumiCycle),它记录光子计数并用所有数据写入二进制(我认为)文件。 它有一个笨重的软件,允许您手动读取文件并将数据导出为.csv。 我想使用 python 脚本绕过软件,该脚本将从一批文件中提取我需要的数据,但我在解码数据文件时遇到了困难。

这是软件导出时数据的样子。 我感兴趣的是计数/秒

Date    Time (hr:min)   Time (days) counts/sec   baseline 
03/18/2022  15:06   0.62917 96.236  104.321
03/18/2022  15:16   0.63611 100.144 104.408
03/18/2022  15:26   0.64306 103.011 104.491
03/18/2022  15:36   0.65    108.001 104.556
03/18/2022  15:46   0.65694 110.415 104.668
03/18/2022  15:56   0.66389 107.923 104.791

这是包含二进制文件导出 csv的文件的保管箱链接

我就文件的结构联系了制造商并得到了这个答案:

“每条记录都是一个由 3 个字符串组成的数组。

Arrays 以 U32 integer 为前缀,包含数组中元素的数量。 字符串以 U32 integer 为前缀,其中包含字符串的长度(以字节为单位)。 整数存储大端。

第一个字符串包含一个 U32 整数数组。 元素的数量(由第一个 U32 指定)取决于您使用的程序版本,但前两个最可能与您相关。 元素是:

 Counts
 Duration (ms)
 Counts 2 (non-zero if color recording)
 channel (1 - 32)
 Sec since midnight Jan 1 1904
 Temperature (°C) x 1000
 Color 1 (RGB) the color selected for displaying the counts
 Color 2 same
 Is Dark Counts? (0 in LC32)

除非您的数据收集计算机设置为不使用夏令时,否则不要使用第 5 个元素。

第二个字符串是文本格式的日期/时间。

第三个字符串包含录制过程中指定的任何注释,因此长度通常为 0。”

所以我读了文件:

f = open("file", "rb")
byte = f.read()
while byte:
    print(byte)
    byte = f.read()

我得到这样的东西:

b'\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1aI\x00\x01\x11"\x00\x00\x00\x00\x00\x00\x00\x13\xdeZP`\x00\x00\x97\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:06\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1bZ\x00\x01\x11\x1f\x00\x00\x00\x00\x00\x00\x00\x13\xdeZR\xb8\x00\x00\x97|\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x1003/18/2022 15:16\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1c\x1e\x00\x01\x10\xf4\x00\x00\x00\x00\x00\x00\x00\x13\xdeZU\x10\x00\x00\x97\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:26\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1d\x80\x00\x01\x11%\x00\x00\x00\x00\x00\x00\x00\x13\xdeZWh\x00\x00\x97\xae\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:36\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1e#\x00\x01\x10\xf1\x00\x00\x00\x00\x00\x00\x00\x13\xdeZY\xc0\x00\x00\x97\xc2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:46\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1du\x00\x01\x10\xf2\x00\x00\x00\x00\x00\x00\x00\x13\xdeZ\\\x18\x00\x00\x97\xea\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:56 etc...

我猜每条记录实际上是这样的:

\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1aI\x00\x01\x11"\x00\x00\x00\x00\x00\x00\x00\x13\xdeZP`\x00\x00\x97\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:06
\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1bZ\x00\x01\x11\x1f\x00\x00\x00\x00\x00\x00\x00\x13\xdeZR\xb8\x00\x00\x97|\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x1003/18/2022 15:16
\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1c\x1e\x00\x01\x10\xf4\x00\x00\x00\x00\x00\x00\x00\x13\xdeZU\x10\x00\x00\x97\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:26
\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1d\x80\x00\x01\x11%\x00\x00\x00\x00\x00\x00\x00\x13\xdeZWh\x00\x00\x97\xae\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:36
\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1e#\x00\x01\x10\xf1\x00\x00\x00\x00\x00\x00\x00\x13\xdeZY\xc0\x00\x00\x97\xc2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:46
\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1du\x00\x01\x10\xf2\x00\x00\x00\x00\x00\x00\x00\x13\xdeZ\\\x18\x00\x00\x97\xea\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:56

我尝试了 bytes.hex() 方法,如果我在每条记录上运行它,我就会得到这个:

000000000000004800000003000000280000000900001b5a0001111f0000000000000013de5a52b80000977c00000000000000000000000a00000010
000000000000004800000003000000280000000900001c1e000110f40000000000000013de5a55100000979000000000000000000000000b00000010                                                          
000000000000004800000003000000280000000900001d80000111250000000000000013de5a5768000097ae00000000000000000000000b00000010
000000000000004800000003000000280000000900001e23000110f10000000000000013de5a59c0000097c200000000000000000000000b00000010
000000000000004800000003000000280000000900001d75000110f20000000000000013de5a5c18000097ea00000000000000000000000b00000010

但我仍然不知道我需要的实际数据在哪里以及为什么某些整数被解释为字符。 关于我可以用来在文件中查找计数/秒数据的方法或函数有什么建议吗?

使用 struct 模块解压缩数据。
您的数据看起来像 6 条记录 - 从文本时间戳推断

第二个字符串是文本格式的日期/时间。

one = b'\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1aI\x00\x01\x11"\x00\x00\x00\x00\x00\x00\x00\x13\xdeZP`\x00\x00\x97\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:06\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1bZ\x00\x01\x11\x1f\x00\x00\x00\x00\x00\x00\x00\x13\xdeZR\xb8\x00\x00\x97|\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x1003/18/2022 15:16\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1c\x1e\x00\x01\x10\xf4\x00\x00\x00\x00\x00\x00\x00\x13\xdeZU\x10\x00\x00\x97\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:26\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1d\x80\x00\x01\x11%\x00\x00\x00\x00\x00\x00\x00\x13\xdeZWh\x00\x00\x97\xae\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:36\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1e#\x00\x01\x10\xf1\x00\x00\x00\x00\x00\x00\x00\x13\xdeZY\xc0\x00\x00\x97\xc2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:46\x00\x00\x00\x00\x00\x00\x00H\x00\x00\x00\x03\x00\x00\x00(\x00\x00\x00\t\x00\x00\x1du\x00\x01\x10\xf2\x00\x00\x00\x00\x00\x00\x00\x13\xdeZ\\\x18\x00\x00\x97\xea\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1003/18/2022 15:56'

Arrays 以 U32 integer 为前缀,包含数组中元素的数量。 字符串以 U32 integer 为前缀,其中包含字符串的长度(以字节为单位)。 整数存储大端。

<first_record_length><first_string_length><first_string><text_timestamp>

文本时间戳字段看起来是固定长度 - 16 个字符。

他们暗示记录/数组长度可能是可变的。 如果您每次都使用相同的仪器和固件,您可能会发现它们始终相同。

>>> # U32 integer sounds like an unsigned long - four bytes
>>> import struct
>>> length = '>L'
>>> (a,) = struct.unpack(length,one[:4])
>>> print(a)
72

三个字符串的第一个数组是 72 字节长。 如果这六个记录中的每一个都是 72 字节,那就是 432 字节。 数据为 452 字节( len(one) ),因此很有希望。

>>> first_record = one[:a]

第三个字符串包含录制过程中指定的任何注释,因此长度通常为 0。

这些字段从记录的第 9 个字节开始 ( <first_record_length><first_string_length><first_string><timestamp> )。 假设最后没有评论和固定长度的时间戳

>>> fields,timestamp = first_record[8:-16],first_record[-16:]
>>> timestamp
b'03/18/2022 15:06'
>>>

这些字段也是无符号长整数( The first string contains an array of U32 integers )。 制造商说有九个可能的字段,因此 36 字节(9 * 4 字节)与该fields变量的长度不太匹配,但可能有一些绒毛。

>>> fields_fmt = '>9L'
>>> stuff = struct.unpack(fields_fmt,fields[:36])
>>> stuff
(40, 9, 6729, 69922, 0, 19, 3730460768, 38840, 0)

我无法判断这是否正确。 您的示例 csv 具有浮点值,数据是整数 - 每个字段都必须进行转换。


查找数据中每个时间戳的位置可以为您提供每条记录有多长的线索。

>>> import re
>>> for m in re.finditer(b'03/18/2022 15:\d\d',one):
...     y = m.end() - x
...     print(y)
...     x += y
... 
72
76
76
76
76
76

原来格式就像 mfg 所说的,看起来每条记录都是 76 字节。

<record_length><field_length>< fields ><timestamp_length><text_timestamp><comment_length>
<  4 bytes    ><  4 bytes   ><44 bytes><    4 bytes     ><   16 bytes   ><   4 bytes    >

数据中的每条记录实际上是 76 个字节。 数据显示每条记录为 72 字节,但每条记录的末尾有 4 个字节( 0000 )。 我不知道这是记录之间的填充还是零的comment_length。

>>>> format = '>LL11LL16sL'
>>> struct.unpack(format,one[:76])         
(72, 3, 40, 9, 6729, 69922, 0, 19, 3730460768, 38840, 0, 0, 11, 16, b'03/18/2022 15:06', 0)
>>> struct.unpack(format,one[76*1:76+76*1])
(72, 3, 40, 9, 7002, 69919, 0, 19, 3730461368, 38780, 0, 0, 10, 16, b'03/18/2022 15:16', 0)
>>> struct.unpack(format,one[76*2:76+76*2]) 
(72, 3, 40, 9, 7198, 69876, 0, 19, 3730461968, 38800, 0, 0, 11, 16, b'03/18/2022 15:26', 0)
>>> struct.unpack(format,one[76*3:76+76*3]) 
(72, 3, 40, 9, 7552, 69925, 0, 19, 3730462568, 38830, 0, 0, 11, 16, b'03/18/2022 15:36', 0)
>>> struct.unpack(format,one[76*4:76+76*4]) 
(72, 3, 40, 9, 7715, 69873, 0, 19, 3730463168, 38850, 0, 0, 11, 16, b'03/18/2022 15:46', 0)

您的示例数据缺少最后一条记录的最后 4 个字节:

>>> struct.unpack('>LL11LL16s',one[76*5:])  
(72, 3, 40, 9, 7541, 69874, 0, 19, 3730463768, 38890, 0, 0, 11, 16, b'03/18/2022 15:56')
>>>

>>> record_length,field_length,*fields,ts_length,timestamp,comment_length = struct.unpack(format,one[:76])
>>> timestamp
b'03/18/2022 15:06'
>>> fields
[40, 9, 6729, 69922, 0, 19, 3730460768, 38840, 0, 0, 11]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM