gpt4 book ai didi

python - 如何在 python3 中有效地将位从一个字节数组打包到另一个字节数组?

转载 作者:行者123 更新时间:2023-11-30 22:11:36 24 4
gpt4 key购买 nike

我在 python 中有一个相当大的字节数组。在最简单的情况下,字节数组仅包含 0 或 1 个值(0x00、0x01),并且数组长度始终是 8 的倍数。如何将这些“位”打包到另一个字节数组中(它不需要是可变的),以便源索引零转到第一个输出字节的 MSB 等。

例如,如果 src = bytearray([1,0,0,0,1,0,0,1, 1,1,1,0,0,0,1,0, 1,1, 1,1,1,1,1,1])所需的输出为 b'\x89\xe2\xff'

我可以使用 for 循环、位移位、或运算和连接来完成此操作,但肯定有更快/更好的内置方法来完成此操作。

在后续问题中,我可能还希望源字节数组包含集合 0-3 中的值,并将这 4 个值一次打包到输出数组中。有办法做到这一点吗?

一般来说,有没有一种方法可以将列表的元素解释为 true 或 false,并将它们一次打包为 8 个字节数组?

最佳答案

尽管听起来很荒谬,但使用内置函数的最快解决方案可能是构建一个字符串并将其传递给 int ,就像计算 int 中 1 位的最快方法一样是 bin(n).count('1') 。而且它也非常简单:

def unbitify_byte(src):
s = ''.join(map(str, src))
n = int(s, 2)
return n.to_bytes(len(src)//8, 'big')

使用 gmpy2 的等效(但稍微复杂一些)代码而不是原生 Python int有点快。

您可以轻松地将其扩展为 2 位值:

def unhalfnybblify_byte(src):
s = ''.join(map(str, src))
n = int(s, 4)
return n.to_bytes(len(src)//4, 'big')
<小时/>

如果您想要更灵活但可能更慢的东西,这里有一个简单的解决方案,使用 ctypes .

如果您了解 C,您可能会发现 8 个单位位字段的结构在这里会派上用场。您可以在 Python 中编写等效的结构类型,如下所示:

class Bits(ctypes.Structure):
_fields_ = [(f'bit{8-i}', ctypes.c_uint, 1) for i in range(8)]

您可以用 8 个全为 0 或 1 的整数构造其中一个:

bits = Bits(*src[:8])

您可以使用丑陋的强制转换或简单的联合将其转换为单个 int:

class UBits(ctypes.Union):
_fields_ = [('bits', Bits), ('i', ctypes.c_uint8)]

i = UBits(Bits(*src[:8])).i

所以现在只是分块的问题 src按大端顺序分成 8 组:

chunks = (src[i:i+8][::-1] for i in range(0, len(src), 8))
dst = bytearray(UBits(Bits(*chunk)).i for chunk in chunks)

如何将其扩展为每个字节四个 2 位字段、或两个 4 位字段、甚至两个 3 位字段和一个 2 位字段应该是非常明显的。

但是,尽管看起来像低级 C 代码,但它可能会更慢。不过,可能值得测试一下它是否足够快适合您的使用。

<小时/>

自定义 C 扩展可能可以做得更好。 PyPI 上有许多位数组类型的模块可供尝试。但如果你想走那条路,numpy是显而易见的答案。没有比这更简单的了:

np.packbits(src)

(A bytearray 作为“类似数组”工作得很好。)

它的速度也很难被击败。

<小时/>

为了进行比较,这里有一些测量结果:

  • 60ns/字节 + 0.3μs:np.packbitsarray上而不是bytearray
  • 60ns/字节 + 1.9μs:np.packbits
  • 440ns/字节 + 3.2μs:for并使用 PyPy 而不是 CPython 进行一些操作
  • 570μs/字节 + 3.8μs:int(…, 2).to_bytes(…)使用 PyPy 而不是 CPython
  • 610ns/字节 + 9.1μs:bitarray
  • 800ns/字节 + 2.9μs:gmpy.mpz(…)…
  • 1.0μs/字节 + 2.8μs:int(…, 2).to_bytes(…)
  • 2.9μs/字节 + 0.2μs:(UBits(Bits(*chunk)) …)
  • 16.μs/字节 + 0.9μs:for和一些小事

关于python - 如何在 python3 中有效地将位从一个字节数组打包到另一个字节数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51373302/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com