简体   繁体   English

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

[英]Extracting data from binary file in python

I have an instrument (LumiCycle from Actimetrics) that records photon counts and writes binary (I think) files with all the data.我有一个仪器(来自 Actimetrics 的 LumiCycle),它记录光子计数并用所有数据写入二进制(我认为)文件。 It has a clunky software that allows you to manually read the file and export the data as.csv.它有一个笨重的软件,允许您手动读取文件并将数据导出为.csv。 I want to bypass the software with a python script that would extract the data I need from a batch of files but I got stuck on decoding the data file.我想使用 python 脚本绕过软件,该脚本将从一批文件中提取我需要的数据,但我在解码数据文件时遇到了困难。

This is what the data looks like when exported by the software.这是软件导出时数据的样子。 The bit that I'm interested in is counts/sec我感兴趣的是计数/秒

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

Here's a dropbox link to a file containing the binary and exported csv这是包含二进制文件导出 csv的文件的保管箱链接

I contacted the manufacturer about the structure of the file and got this answer:我就文件的结构联系了制造商并得到了这个答案:

"Each record is an array of 3 strings. “每条记录都是一个由 3 个字符串组成的数组。

Arrays are prefixed by a U32 integer containing the number of elements in the array. Arrays 以 U32 integer 为前缀,包含数组中元素的数量。 Strings are prefixed by a U32 integer containing the length of the string in bytes.字符串以 U32 integer 为前缀,其中包含字符串的长度(以字节为单位)。 integers are stored Big-endian.整数存储大端。

The first string contains an array of U32 integers.第一个字符串包含一个 U32 整数数组。 The number of elements (as specified by the first U32) depends on what version of the program you are using, but the first two are the ones that are most likely relevant to you.元素的数量(由第一个 U32 指定)取决于您使用的程序版本,但前两个最可能与您相关。 The elements are:元素是:

 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)

Do not use the 5th element unless your data collection computer is set not to use Daylight Savings Time.除非您的数据收集计算机设置为不使用夏令时,否则不要使用第 5 个元素。

The second string is the date/time in text format.第二个字符串是文本格式的日期/时间。

The third string contains any comments specified during recording, so the length is usually 0."第三个字符串包含录制过程中指定的任何注释,因此长度通常为 0。”

So I read the file:所以我读了文件:

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

And I get something like this:我得到这样的东西:

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

I'm guessing each record is actually this:我猜每条记录实际上是这样的:

\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

I tried the bytes.hex() method and I get this if I run it on each record:我尝试了 bytes.hex() 方法,如果我在每条记录上运行它,我就会得到这个:

000000000000004800000003000000280000000900001b5a0001111f0000000000000013de5a52b80000977c00000000000000000000000a00000010
000000000000004800000003000000280000000900001c1e000110f40000000000000013de5a55100000979000000000000000000000000b00000010                                                          
000000000000004800000003000000280000000900001d80000111250000000000000013de5a5768000097ae00000000000000000000000b00000010
000000000000004800000003000000280000000900001e23000110f10000000000000013de5a59c0000097c200000000000000000000000b00000010
000000000000004800000003000000280000000900001d75000110f20000000000000013de5a5c18000097ea00000000000000000000000b00000010

But I still don't know where is the actual data I need and why some of the integers are interpreted as chars.但我仍然不知道我需要的实际数据在哪里以及为什么某些整数被解释为字符。 Any suggestion on methods or functions I can use to find the count/sec data in the file?关于我可以用来在文件中查找计数/秒数据的方法或函数有什么建议吗?

Use the struct module to unpack the data.使用 struct 模块解压缩数据。
Your data looks like 6 records - deduced from the text timestamps您的数据看起来像 6 条记录 - 从文本时间戳推断

The second string is the date/time in text format.第二个字符串是文本格式的日期/时间。

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 are prefixed by a U32 integer containing the number of elements in the array. Arrays 以 U32 integer 为前缀,包含数组中元素的数量。 Strings are prefixed by a U32 integer containing the length of the string in bytes.字符串以 U32 integer 为前缀,其中包含字符串的长度(以字节为单位)。 integers are stored Big-endian.整数存储大端。

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

The text timestamp field looks like it is fixed length - 16 characters.文本时间戳字段看起来是固定长度 - 16 个字符。

They are implying that the record/array lengths might be variable.他们暗示记录/数组长度可能是可变的。 You might find that they are consistently the same if you are using the same instrument and firmware every time.如果您每次都使用相同的仪器和固件,您可能会发现它们始终相同。

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

The first array of three strings is 72 bytes long.三个字符串的第一个数组是 72 字节长。 If each of the six records is 72 bytes, that's 432 bytes.如果这六个记录中的每一个都是 72 字节,那就是 432 字节。 The data is 452 bytes ( len(one) ) so that's promising.数据为 452 字节( len(one) ),因此很有希望。

>>> first_record = one[:a]

The third string contains any comments specified during recording, so the length is usually 0.第三个字符串包含录制过程中指定的任何注释,因此长度通常为 0。

The fields begin at the ninth byte of the record ( <first_record_length><first_string_length><first_string><timestamp> ).这些字段从记录的第 9 个字节开始 ( <first_record_length><first_string_length><first_string><timestamp> )。 Assuming no comments and a fixed length time stamp at-the-end假设最后没有评论和固定长度的时间戳

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

The fields are also unsigned longs ( The first string contains an array of U32 integers ).这些字段也是无符号长整数( The first string contains an array of U32 integers )。 The mfg said that there are nine possible fields so 36 bytes (9 * 4 bytes) which doesn't quite match up with the length of that fields variable but maybe there is some fluff.制造商说有九个可能的字段,因此 36 字节(9 * 4 字节)与该fields变量的长度不太匹配,但可能有一些绒毛。

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

I cannot tell if that is correct.我无法判断这是否正确。 Your example csv has floats for values and the data is ints - there must be a conversion for each of the fields.您的示例 csv 具有浮点值,数据是整数 - 每个字段都必须进行转换。


Finding the location of each timestamp in the data can give you a clue to how long each record is.查找数据中每个时间戳的位置可以为您提供每条记录有多长的线索。

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

Turns out the format is just like mfg stated and it looks like each record is 76 bytes.原来格式就像 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    >

Each record in your data is actually 76 bytes.数据中的每条记录实际上是 76 个字节。 The data says each record is 72 bytes but there is a 4 bytes at the end of each ( 0000 ).数据显示每条记录为 72 字节,但每条记录的末尾有 4 个字节( 0000 )。 I can't tell if that is padding between records or the comment_length which is zero.我不知道这是记录之间的填充还是零的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)

Your example data is missing the last 4 bytes of of the last record:您的示例数据缺少最后一条记录的最后 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