Python字节数组转换为位数组

10

我希望用Python和scapy解析一些数据。因此,我必须分析单个位。但是目前我有一些包含一些有效载荷的UDP数据包,例如:

bytes = b'\x18\x00\x03\x61\xFF\xFF\x00\x05\x42\xFF\xFF\xFF\xFF'

有没有一种优雅的方式将字节转换,以便我可以像这样访问单个位:

bytes_as_bits = convert(bytes)
bit_at_index_42 = bytes_as_bits[42]

那么,例如位 8 会是什么呢?第二个字节的最高有效位?还是最低有效位? - Eugene Sh.
你尝试过类似于''.join(f'{byte:b}' for byte in bytes)这样的东西吗? - vaultah
6个回答

11

那会起作用:

def access_bit(data, num):
    base = int(num // 8)
    shift = int(num % 8)
    return (data[base] >> shift) & 0x1

如果你想创建一个二进制数组,可以像这样使用:

[access_bit(data,i) for i in range(len(data)*8)]

7
如果您想获得位字符串,或者不想自己创建函数,我建议使用 format()和 ord()。让我举一个简单的例子来说明。
bytes = '\xf0\x0f'
bytes_as_bits = ''.join(format(ord(byte), '08b') for byte in bytes)

这应该输出:'1111000000001111'

如果你想要最低有效位(LSB)在前,你可以翻转format()的输出,所以:

bytes = '\xf0\x0f'
bytes_as_bits = ''.join(format(ord(byte), '08b')[::-1] for byte in bytes)

这应该输出:'0000111111110000'

现在你想使用 b'\xf0\x0f' 而不是 '\xf0\x0f'。对于 Python2,代码的工作方式相同,但对于 Python3,您必须摆脱 ord(),因此:

bytes = b'\xf0\x0f'
bytes_as_bits = ''.join(format(byte, '08b') for byte in bytes)

反转字符串同样是一个问题。

我在这里找到了format()功能的相关信息 反转字符串 ([::-1]) 的功能可以在这里找到


这个回答和被接受的回答各有优点。 - Valen

4
嗯,在Python中没有内置的bits类型,但你可以这样做
>>> bin(int.from_bytes(b"hello world", byteorder="big")).lstrip('0b')
'110100001100101011011000110110001101111001000000111011101101111011100100110110001100100'

使用.lstrip('0b')方法可以移除bin()函数输出中的任何前导'0b'字符。

你的答案如何处理大字节数组? - vaultah
在这种特定情况下,有效载荷似乎并不大。 - hello world

2
>>> n=17
>>> [(n & (1<<x))>>x for x in [7,6,5,4,3,2,1,0]]
[0, 0, 0, 1, 0, 0, 0, 1]

0
我只需要使用简单的lambda表达式将字节转换成字符串:
>>> bytes = b'\x18\x00\x03\x61\xFF\xFF\x00\x05\x42\xFF\xFF\xFF\xFF'
>>> convert = lambda x: f"{int.from_bytes(x, 'big'):b}"
>>> bytes_as_bits = convert(bytes)
>>> bytes_as_bits[42]
'1'
>>> _

'big' 是要使用的字节顺序。Python官方文档描述如下:

byteorder参数确定用于表示整数的字节顺序。 如果byteorder为“big”,则最高有效字节位于字节数组的开头。如果byteorder为“little”,则最高有效字节位于字节数组的末尾。要请求主机系统的本机字节顺序,请使用sys.byteorder作为字节顺序值。


0
为了扩展@Liran的答案,我添加了一个输入参数byteorder,默认为“big”。 注意,我不是指字节内的位打包。
def access_bit(b: bytearray, n: int, byteorder: str = "big") -> int:
    """
    Returns the boolean value of the nth bit (n) from the byte array (b).
    The byteorder argument accepts the literal strings ['little', 'big'] and
    refers to the byte order endianness
    """
    base = int(n // 8)
    shift = int(n % 8)
    if byteorder == "big":
        return (b[-base - 1] >> shift) & 0x1
    elif byteorder == "little":
        return (b[base] >> shift) & 0x1
    else:
        raise KeyError("byteorder only recognises 'big' or 'little'")

access_bit(b, 0) 返回假设为 大端序最低有效位 所在的 最低有效字节

access_bit(b, 7) 返回假设为 大端序最高有效位 所在的 最低有效字节

access_bit(b, 0, 'little') 返回指定为 小端序最低有效位 所在的 最低有效字节

access_bit(b, 7) 返回假设为 小端序最高有效位 所在的 最低有效字节

如果指定的索引 n 超出了 bytearray 的范围,则会导致错误(例如,access_bit(b'\x05\x01', 16) 将导致错误,因为 bytearray 的最大索引为 15)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接