Python使用struct.unpack解析GWI二进制文件头结构体GWI_file_header_struct
结构体转struct.unpack逻辑校验与后续数据解析方案
现有实现逻辑合理性说明
你已经可以正确读取头长度、字节序、通道名称,说明头解析的核心逻辑符合文件格式要求,现有逻辑是正确的,核心对齐规则如下:
- 第一步读取前4字节获取单个头的字节长度,该逻辑匹配文件定义。你可以直接通过这4字节的解析结果确认全局字节序:如果用小端模式
struct.unpack('<I', 前4字节)[0]得到516,说明整个文件是小端存储;如果用大端模式得到516则是大端存储,无需硬编码字节序。 - 读取第一个头的
channelsPerFile字段获取总通道数N,再循环读取N-1个剩余头的逻辑是正确的,只要每个头的长度都和第一个头的长度一致,就不会出现偏移错误。
GWI_file_header_struct转struct格式串的通用规则
你可以对照INET_INT.H头文件中的结构体定义,按以下规则匹配struct格式符,格式串开头加字节序前缀:小端加<,大端加>,本地字节序加=:
- char/unsigned char:对应
B(无符号)/b(有符号),占1字节 - short/unsigned short:对应
H(无符号)/h(有符号),占2字节 - int/unsigned int:对应
I(无符号)/i(有符号),占4字节 - float:对应
f,占4字节 - char arr[N]:对应
{N}s,例如char channelName[16]对应16s,读取后用.strip(b'\x00').decode()转为字符串
注意结构体的内存对齐规则:如果凑出来的格式串总长度不等于你读取到的单个头长度,需要在对应位置加x作为填充字节,例如两个int字段之间有2字节对齐填充,就加2x跳过这2字节。
举个参考示例,假设头文件结构体定义如下:
typedef struct { uint32_t headerLen; uint32_t channelsPerFile; char channelName[16]; float sampleRate; // 其余字段总长度为488字节 } GWI_file_header_struct;
对应的小端模式格式串为:<I I 16s f 488x,总长度为4+4+16+4+488=516字节,和单个头长度匹配。
后续交错32位浮点数据解析方案
读完N个头后,文件偏移位置就是数据区起始位置,按以下逻辑处理:
- 计算数据区总字节数:文件总大小减去头数组总长度(N * 单个头长度),每个采样点为4字节float,总采样点数 = 数据区总字节数 // 4
- 交错存储规则为
[通道1第1个点, 通道2第1个点, ..., 通道N第1个点, 通道1第2个点, 通道2第2个点...],按该规则拆分数据即可 - 参考实现代码:
import struct import os # 替换为你确认的字节序前缀、头格式串 endian = '<' header_format = f"{endian}I I 16s f 488x" single_header_len = struct.calcsize(header_format) file_path = "你的二进制文件路径" file_size = os.path.getsize(file_path) with open(file_path, 'rb') as f: # 读取所有头结构体 headers = [] # 读第一个头获取总通道数 first_header = struct.unpack(header_format, f.read(single_header_len)) # 替换为channelsPerFile字段在结构体中的实际索引 channel_count = first_header[1] headers.append(first_header) # 读取剩余的头 for _ in range(channel_count - 1): headers.append(struct.unpack(header_format, f.read(single_header_len))) # 读取并解析数据区 data_bytes = f.read() total_sample_count = len(data_bytes) // 4 data_format = f"{endian}{total_sample_count}f" all_samples = struct.unpack(data_format, data_bytes) # 按通道拆分数据 channel_data = [[] for _ in range(channel_count)] for idx, sample in enumerate(all_samples): channel_idx = idx % channel_count channel_data[channel_idx].append(sample)
- 如果文件过大不想一次性读入内存,可以按块读取,每次读取
通道数 * 4 * 单块采样数的字节,解析后拆分到对应通道即可。
内容的提问来源于stack exchange,提问作者waterab
相关产品推荐
相关产品推荐

