使用Python Numpy解析包含BCD(二进制编码十进制)值的二进制文件

使用Python Numpy解析包含BCD(二进制编码十进制)值的二进制文件,python,numpy,Python,Numpy,我有一个二进制文件,其中一些字段编码为BCD(二进制编码的十进制)。示例如下 14 75 26 58 87 7F(十六进制格式的原始字节) 我使用(np.void,6)从二进制文件读取和转换,下面是我得到的输出 b'\x14\x75\x26\x58\x87\x7F' 但是我希望得到的输出是'14752658877',没有使用numpy填充字符'F' 代码如下: 打开(文件名为“rb”)作为f: 此外,输入文件包含许多固定长度的二进制记录。使用numpy将其转换并存储为ascii文件的有效方法是什

我有一个二进制文件,其中一些字段编码为BCD(二进制编码的十进制)。示例如下

14 75 26 58 87 7F(十六进制格式的原始字节)

我使用(np.void,6)从二进制文件读取和转换,下面是我得到的输出

b'\x14\x75\x26\x58\x87\x7F'

但是我希望得到的输出是'14752658877',没有使用numpy填充字符'F'

代码如下: 打开(文件名为“rb”)作为f:


此外,输入文件包含许多固定长度的二进制记录。使用numpy将其转换并存储为ascii文件的有效方法是什么

我不知道numpy是否能以某种方式加速这一过程,但可以快速构造一个特定的函数:

fastDict = {16*(i//10)+(i%10):i for i in range(100)}

def bcdToInteger(bcd):
    result = 0
    while bcd and bcd[0] in fastDict:
        result *= 100
        result += fastDict[bcd[0]]
        bcd = bcd[1:]
    if bcd and bcd[0] & 0xf0 <= 0x90:
        result *= 10
        result += bcd[0]>>4
        if bcd[0] & 0xf <= 9:
            result *= 10
            result += bcd[0] & 0x0f
    return result

>>> print (bcdToInteger(b'\x14\x75\x26\x58\x87\x7F'))  # your sequence
14752658877
>>> print (bcdToInteger(b'\x12\x34\xA0'))   # first invalid nibble ends
1234
>>> print (bcdToInteger(b'\x00\x00\x99'))   # and so does an end of string
99
>>> print (bcdToInteger(b'\x1F'))           # a single nibble value
1
fastDict={16*(i//10)+(i%10):i代表范围(100)内的i)
def bcd指示灯(bcd):
结果=0
而fastDict中的bcd和bcd[0]为:
结果*=100
结果+=fastDict[bcd[0]]
bcd=bcd[1:]
如果bcd和bcd[0]&0xf0>4
如果bcd[0]&0xf>>打印(bcdToInteger(b'\x14\x75\x26\x58\x87\x7F'))#您的顺序
14752658877
>>>打印(bcdToInteger(b'\x12\x34\xA0'))#第一个无效的半字节结束
1234
>>>打印(bcdToInteger(b'\x00\x00\x99'))#以及字符串的结尾
99
>>>打印(bcdToInteger(b'\x1F'))#单个半字节值
1.
只要您继续向它输入有效的BCD字节,它就会将结果乘以100并添加两个新数字。只有最后一个字节需要进一步检查:如果最高的半字节是有效的,那么到目前为止的结果将乘以10,该半字节将被相加。如果最低半字节也有效,则重复此操作


fastDict
是为了加快速度。它是一个字典,返回从
00
99
的所有100个十六进制字节的正确值,因此实际计算的数量尽可能少。您可以不使用字典,但这意味着您必须在
if
块中对每个字节进行比较和计算。

显示示例代码。下面是代码:open(filename,“rb”)为f:True:chunk=f.read(chunksize)if(chunk):dt=np.dtype([('a','b'),('b','>i4'),('c','S15'),('d',np.str,7),('e','S7'),('f',np.void,6)]x=np.frombuffer(chunk,dtype=dt)print(x)其他:请编辑原始问题,添加上述代码,保留所有格式和缩进。你好,李嘉图,我已编辑原始问题以添加代码。F不是填充字符。这是十六进制值的一部分。感谢您的解决方案。我正在使用binascii.hexlify(bcdvalue).decode('utf-8').rstrip('f')来获得首选结果。但我正在寻找高效的解决方案,因为我有很多这样的专栏。我们每天的记录量接近10亿条。@RajKB:我觉得我的解决方案相当有效。正如您在中所看到的,它们使用昂贵的位移位,并且每个字节比较两次;我的代码避免了这一点。然而,使用C语言中的自定义扩展可以编写更快的代码,但我不打算尝试。嗨,我正在尝试您的解决方案。但是我在运行代码时遇到了以下错误。你能检查一下吗。文件“”,第7行,在bcdToInteger中,如果bcd和bcd[0]&0xf0,请忽略上面的注释。是的,它忽略了。非常感谢你的回答。
fastDict = {16*(i//10)+(i%10):i for i in range(100)}

def bcdToInteger(bcd):
    result = 0
    while bcd and bcd[0] in fastDict:
        result *= 100
        result += fastDict[bcd[0]]
        bcd = bcd[1:]
    if bcd and bcd[0] & 0xf0 <= 0x90:
        result *= 10
        result += bcd[0]>>4
        if bcd[0] & 0xf <= 9:
            result *= 10
            result += bcd[0] & 0x0f
    return result

>>> print (bcdToInteger(b'\x14\x75\x26\x58\x87\x7F'))  # your sequence
14752658877
>>> print (bcdToInteger(b'\x12\x34\xA0'))   # first invalid nibble ends
1234
>>> print (bcdToInteger(b'\x00\x00\x99'))   # and so does an end of string
99
>>> print (bcdToInteger(b'\x1F'))           # a single nibble value
1