【问题标题】:Using Python to manipulate 31-bits使用 Python 操作 31 位
【发布时间】:2016-12-14 12:14:56
【问题描述】:

我有一个规范,其中概述了指令应如何通过串行发送。

目前我正在制作将通过连接的数据包。

数据包的一个片段,需要一个 32 位(4 字节)的二进制数。 前 31 位是“数据”,最后一位只是一个标志。

因此,数据中可以容纳的最大十进制数是:2147483647 (2^31)。数据永远不会比这更大,酷!

我的问题是如何将数据编码为 31 位二进制,然后设置最后一位以启用标志?

假设我的数据是 7AAAAAAA 将其转换为 31 位二进制然后在末尾添加 1 或 0 的理想方法是什么?

编辑 - 我使用的是 Python 3.4

【问题讨论】:

  • 查看struct 模块以及位修改运算符|
  • 在这种情况下使用 struct 可能会出现问题,因为您的数据编号不是 4 位整数。结构仅对完整字节进行操作。
  • 对于硬编码数据,您可以执行(0x7AAAAAAA<<1) | flag 之类的操作。但无论如何,您都应该使用struct 以可识别字节序的方式处理字节转换。
  • Python 中的位摆弄可能有点棘手,因为我们没有无符号整数,而且我们的整数不是固定字节长度的。但是,仔细使用struct 确实不是问题。

标签: python binary hex python-3.4 pyserial


【解决方案1】:

看看这是否有效,类似于其他答案,但考虑了原始位长度。

定义最终位数

>>> bits = 16

以字节文字开头

>>> a = b'10'

转换为int

>>> b = int(a, base = 16)

左移到所需的位长

>>> shift = bits - b.bit_length()
>>> c = b << shift

添加标志

>>> d = c | 1

>>> 
>>> a
b'10'
>>> b
16
>>> c
32768
>>> d
32769

>>> bin(b)
'0b10000'
>>> bin(c)
'0b1000000000000000'
>>> bin(d)
'0b1000000000000001'
>>>

【讨论】:

    【解决方案2】:

    我认为您可以使用二进制移位将您的标志添加到数字:

    a = 0x7AAAAAAA                # 2058005162 = 0b1111010101010101010101010101010
    f = 1                         # 1 = 0b1
    
    packet = a + (f << 31)        # a + 0b10000000000000000000000000000000
    bin(packet)                   # 0b11111010101010101010101010101010
    

    要解压,你可以像这样使用掩码和二进制 AND:

    mask = (1 << 31) - 1          # 2147483647 = 0b1111111111111111111111111111111
    a = packet & mask             # 2058005162 = 0b1111010101010101010101010101010
    f = packet >> 31              # 1 = 0b1
    

    【讨论】:

    • 或者类似packet = (a &lt;&lt; 1) | f,如果你想要最后的标志
    【解决方案3】:

    我唯一想到的就是对字符串的操作

    假设我们有两个变量:

    >>> data = '7AAAAAAA'
    >>> flag = '1'
    

    将数据十六进制转换为数字

    >>> num = int(data, 16)
    >>> num
    2058005162
    

    将数字转换为字符串二进制表示:

    >>> bin_num = bin(num)
    >>> bin_num
    '0b1111010101010101010101010101010'
    

    在末尾添加标志

    >>> bin_num += flag
    >>> bin_num
    '0b11110101010101010101010101010101'
    

    评估字符串以获取数字并转换回十六进制或您需要的任何内容:

    >>> eval(bin_num)
    4116010325
    

    #edit1

    为了将值扩展到 4 字节,您可以使用:

    >>> final_val = eval(bin_num)
    >>> int.to_bytes(final_val, 4, 'big')
    b'\xf5UUU'
    

    #edit2

    def convert(data, flag):
        with_flag = eval(bin(int(data, 16)) + flag)
        return int.to_bytes(with_flag, 4, 'big')
    
    def unconvert(byte_data):
        bin_str = bin(int.from_bytes(byte_data, 'big'))
        flag = bin_str[-1]
        data = bin_str[:-1]
        return (hex(eval(data)), flag)
    

    【讨论】:

    • 这可行,我可以使用binascii 对二进制数据进行编码。但是,如果数据只是FF,那么二进制表示就不够大了,也许是一种填充数据以始终使用 31 位二进制表示的方法?
    • 你可以使用int.to_bytes(final_number, 4, 'big')来使用big-endian,并扩展到4个字节
    猜你喜欢
    • 2017-01-27
    • 2011-11-18
    • 1970-01-01
    • 2021-03-09
    • 2011-02-23
    • 1970-01-01
    • 2020-07-06
    • 2013-06-13
    相关资源
    最近更新 更多