简体   繁体   English

转换金额(int)到BCD

[英]Convert amount (int) to BCD

I need to convert an Int left padded 6 bytes (amount) to a BCD in Python. 我需要将Int左填充的6个字节(数量)转换为Python中的BCD。

int = 145
expect = "\x00\x00\x00\x00\x01\x45"

The closest I come is with this code (but it needs to loop in byte pair): 我最接近的是此代码(但需要在字节对中循环):

def TO_BCD(value):
    return chr((((value / 10) << 4) & 0xF0) + ((value % 10) & 0x0F))

int = 145
TO_BCD(int) # => "\x00\x00\x00\x00\x01\x45" (expected)

Here's an example. 这是一个例子。

script0.py : script0.py

#!/usr/bin/env python3

import sys


def bcd(value, length=0, pad='\x00'):
    ret = ""
    while value:
        value, ls4b = divmod(value, 10)
        value, ms4b = divmod(value, 10)
        ret = chr((ms4b << 4) + ls4b) + ret
    return pad * (length - len(ret)) + ret


def bcd_str(value, length=0, pad='\x00'):
    value_str = str(value)
    value_str = ("0" if len(value_str) % 2 else "") + value_str
    ret = ""
    for i in range(0, len(value_str), 2):
        ms4b = ord(value_str[i]) - 0x30
        ls4b = ord(value_str[i + 1]) - 0x30
        ret += chr((ms4b << 4) + ls4b)
    return pad * (length - len(ret)) + ret


def main():
    values = [
        145,
        5,
        123456,
    ]
    for value in values:
        print("{0:d} - [{1:s}] - [{2:s}]".format(value, repr(bcd(value, length=6)), repr(bcd_str(value, length=6))))

    # Bonus
    speed_test = 1
    if speed_test:
        import timeit  # Anti pattern: only import at the beginning of the file
        print("\nTesting speed:")
        stmt = "bcd({0:d})".format(1234567890 ** 32)
        count = 100000
        for func_name in ["bcd", "bcd_str"]:
            print("    {0:s}: {1:.03f} secs".format(func_name, timeit.timeit(stmt, setup="from __main__ import {0:s} as bcd".format(func_name), number=count)))


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main()
    print("\nDone.")

Output : 输出

 [cfati@CFATI-5510-0:e:\\Work\\Dev\\StackOverflow\\q057476837]> "e:\\Work\\Dev\\VEnvs\\py_064_03.07.03_test0\\Scripts\\python.exe" script0.py Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32 145 - ['\\x00\\x00\\x00\\x00\\x01E'] - ['\\x00\\x00\\x00\\x00\\x01E'] 5 - ['\\x00\\x00\\x00\\x00\\x00\\x05'] - ['\\x00\\x00\\x00\\x00\\x00\\x05'] 123456 - ['\\x00\\x00\\x00\\x124V'] - ['\\x00\\x00\\x00\\x124V'] Testing speed: bcd: 17.107 secs bcd_str: 8.021 secs Done. 

Notes : 注意事项

  • Since you're working with packed BCD , each digit will be stored in 4 bits, and thus 2 digits will take one byte 由于您使用打包的 BCD ,因此每个数字将以4位存储,因此2个数字将占用一个字节
  • The algorithm is simple: split the number in 2 digit groups, in each group the 1 st ( M ost S ignificant ) digit will be shifted to the left by 4 bits, and then the 2 nd ( L east S ignificant ) one will be added - this will be the char 's ASCII code 该算法是简单的:在2个组分割的数目,每个组的1 (M OST 小号 ignificant)位将由4位被移位到左边,然后第2 (L 小号 ignificant)之一将是添加-这将是charASCII
  • The output might look a bit different than what you're expecting, but that's only due display formatting: for example capital letter ' E ' char has the ASCII code 0x45 ( 69 ), and can also be written as ' \\x45 ', so the output is correct 输出可能看上去有点比你期待什么不同,但是这仅仅是由于显示格式:比如大写字母“E” charASCII0×45(69),也可以写成“\\ X45”,所以输出正确
  • There are 2 implementations: 有2种实现:

    • bcd - uses arithmetical operations bcd-使用算术运算
    • bcd_str - uses operations on strings bcd_str-使用字符串操作

    The speed test (at the end of main ) yields surprising results: the 2 nd (string) variant is faster (by a factor of ~2 ). 速度测试(位于main的末尾)产生了令人惊讶的结果: 第二个 (字符串)变体更快约2倍)。 A brief explanation would be that (in Python ,) modulo operation is expensive (slow) on large numbers. 一个简短的解释是(在Python中), 运算在大量上是昂贵的(缓慢的)。

This seems fairly simple, and gets the answer you were looking for. 这似乎很简单,并且可以找到您想要的答案。 Just isolate each pair of digits and convert to ASCII. 只需隔离每对数字并转换为ASCII。

If I were doing this in high volume then I'd probably build a table (perhaps in numpy) of all the possible 100 values per byte and index it with each pair of digits in the input. 如果我要大量执行此操作,则可能会构建一个表(每个字节可能包含100个值)(也许以numpy表示),并用输入中的每对数字对其进行索引。

m = 145
print(''.join(f"\\x{m // 10**i % 10}{m // 10**(i-1) % 10}" for i in range(11, -1, -2)))

Output, although it's just a string, not any internal BCD representation 输出,尽管只是一个字符串,而不是任何内部BCD表示形式

\x00\x00\x00\x00\x01\x45

Along the same lines, you can pack the BCD into a byte string. 同样,您可以将BCD打包为一个字节字符串。 When printed, Python will interpret BCD 45 as a capital E 打印后,Python会将BCD 45解释为大写E

import struct
m = 145

packed = struct.pack('6B', *[(m // 10**i % 10 << 4) + (m // 10**(i-1) % 10) for i in range(11, -1, -2)])
print(packed)
print(''.join(f"\\{p:02x}" for p in packed))

Output 输出量

b'\x00\x00\x00\x00\x01E'
\00\00\00\00\01\45

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM