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