如何解码编码未知的.dat二进制文件?
读取二进制.dat文件到Pandas的解决思路
问题背景
我需要将多个.dat二进制文件读取到Pandas中,但无法确定其编码格式。目前唯一能让Pandas不抛出异常的代码如下:
for chunk in pd.read_csv(data_file, index_col=False, sep=r'\s\s+', chunksize=10**3, engine="python"): print(chunk) break
执行后返回大量乱码:
4.2DABCDEFGHSnRbTs 0 ����@�\t�<\t��P��... 1 N DEN TAA 2 ��@<<J8TE�nnJ8@��N TA���`A"�UR... 3 �@DF((4PP�PP��DEZZ�PX22�PDE22u0@A�... 4 u0TEݠ<'Pd<��@ .. ... 737 �PTF\ 738 pTB�(("�Pn22�pTE\�TF� 739 �8@F�PҨ@� 740 n�`DP��P��[`DA�x�DA�ZZ]�Td<(w... 741 "�URc,�8�A<,(TUc�ix@��m`T�... [742 rows x 1 columns]
我读取了前三个文件的前100字节二进制数据:
with open(data_file, "rb") as f: data = f.read(100) print(data)
结果如下:
b'\x00\x034.2\x00\x01D\x00\x08ABCDEFGH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06SnRbTs\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x00\x00\x00\x00\xbb\xbb\x00\x00\x00\xf6\xca@\xd2\x0b\x00\x00\x00\x00\x00\t\xf1<\x00\x00\x00\x00\x00\t\xef\xd4\x00\x00\x00\x00\x00\x01L\x08P\x05\x0e\xe1\xf0P\xd6\xd8D' b'\x00\x034.2\x00\x01D\x00\x08ABCDEFGH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06SnRbTs\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x00\x00\x00\x00\xbb\xbb\x00\x00\x00\xf6\xcaA\x04d\x00\x00\x00\x00\x00\x08\xea&\x00\x00\x00\x00\x00\x08\xe8\xd2\x00\x00\x00\x00\x00\x01\xd4\xc0P\x06\x0b~\xb4\x8c\x00\x1e]' b'\x00\x034.2\x00\x01D\x00\x08ABCDEFGH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06SnRbTs\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x00\x00\x00\x00\xbb\xbb\x00\x00\x012\x81\xe1\x0b,\x00\x00\x00\x02\xad\xd9\xd9\xb0\x00\x00\x00\x02\xad\xd9\xd9\xb0\x00\x00\x00\x00\x00\x01\x86\xa0R$\nE\x00,\x8e@\x00'
我试过用chardet、charset_normalizer识别编码,也手动测试了所有Python支持的编码格式执行data.decode(enc, 'ignore'),结果全是乱码。我期望解析后的数据格式为:
datetime,ask,bid,vol 2017-01-02,134.44,134.58,89 2017-01-03,134.34,134.58,85 ...
解决思路
1. 先明确:这不是文本编码问题
从二进制数据看,文件里有大量\x00空字节和非ASCII控制字符,这根本不是文本文件,所以用文本编码解码、pd.read_csv这类文本读取方法完全不适用。这类.dat文件几乎都是自定义二进制结构化格式,或者是行业/软件特定的二进制协议。
2. 先解析固定文件头
三个文件的开头完全一致,这是典型的文件头标识:
\x00\x03是2字节的长度前缀,对应后面的4.2(刚好3个ASCII字符)\x00\x01对应后面的D(1个字符)\x00\x08对应后面的ABCDEFGH(8个字符)\x00\x06对应后面的SnRbTs(6个字符)
这说明文件采用**「长度前缀+内容」**的结构化格式,字节顺序是大端(因为\x00\x03表示3,高位字节在前)。
3. 推断数据块结构并手动解析
结合你期望的datetime,ask,bid,vol结构,数据块大概率包含时间戳、两个浮点数值、一个整数。可以用Python的struct模块尝试解析:
import struct import pandas as pd def parse_dat_file(file_path): records = [] with open(file_path, 'rb') as f: # 解析文件头固定字段 len_prefix = struct.unpack('!H', f.read(2))[0] version = f.read(len_prefix).decode('ascii') len_prefix = struct.unpack('!H', f.read(2))[0] field_d = f.read(len_prefix).decode('ascii') len_prefix = struct.unpack('!H', f.read(2))[0] field_abc = f.read(len_prefix).decode('ascii') # 跳过连续空字节 while True: b = f.read(1) if b != b'\x00': f.seek(-1, 1) break len_prefix = struct.unpack('!H', f.read(2))[0] field_sn = f.read(len_prefix).decode('ascii') # 定位到数据区(跳过\xbb\xbb前的冗余字节) while True: b = f.read(1) if b == b'\xbb' and f.read(1) == b'\xbb': break # 假设每个记录格式:8字节双精度时间戳 + 4字节单精度ask + 4字节单精度bid + 4字节无符号整数vol record_format = '!dffI' record_size = struct.calcsize(record_format) while True: data = f.read(record_size) if len(data) < record_size: break timestamp, ask, bid, vol = struct.unpack(record_format, data) # 转换时间戳为datetime,可根据实际调整单位 dt = pd.to_datetime(timestamp, unit='s') records.append({ 'datetime': dt, 'ask': ask, 'bid': bid, 'vol': vol }) return pd.DataFrame(records) # 测试解析 df = parse_dat_file('your_file.dat') print(df.head())
4. 调整解析规则的技巧
- 用二进制编辑器(如HxD、010 Editor)打开多个文件,对比相同位置的字节,找重复模式,推断记录长度和字段类型。
- 如果解析出的数值不合理(比如时间戳对应异常日期),调整
struct格式符:比如把大端!改成小端<,双精度d改成单精度f,整数类型I改成i等。 - 若知道文件来源(如某软件导出、某接口获取),直接查对应格式文档,这是最快的方法。
5. 备选工具:用Numpy读取
如果手动解析麻烦,可尝试用numpy.fromfile读取二进制数据,再转成DataFrame:
import numpy as np import pandas as pd # 定义数据结构,需根据实际调整 dtype = np.dtype([ ('timestamp', '>f8'), ('ask', '>f4'), ('bid', '>f4'), ('vol', '>u4') ]) # 跳过文件头长度(需根据实际计算) data = np.fromfile('your_file.dat', dtype=dtype, offset=60) df = pd.DataFrame(data) df['datetime'] = pd.to_datetime(df['timestamp'], unit='s')
内容的提问来源于stack exchange,提问作者965311532
相关产品推荐
相关产品推荐

