在Python中将位转换为字节
在Python3.x中,我试图将位字符串转换为字节字符串。在每个字节中,位从高阶填充到低阶。如有必要,最后一个字节用零填充。位字符串最初存储为布尔或整数(0或1)的“集合”,我想返回0-255范围内整数的“集合”。所谓集合,我指的是一个列表或类似的对象,但不是字符串:例如,下面的函数返回一个生成器 到目前为止,我能得到的最快的结果如下:在Python中将位转换为字节,python,python-3.x,optimization,bit-manipulation,Python,Python 3.x,Optimization,Bit Manipulation,在Python3.x中,我试图将位字符串转换为字节字符串。在每个字节中,位从高阶填充到低阶。如有必要,最后一个字节用零填充。位字符串最初存储为布尔或整数(0或1)的“集合”,我想返回0-255范围内整数的“集合”。所谓集合,我指的是一个列表或类似的对象,但不是字符串:例如,下面的函数返回一个生成器 到目前为止,我能得到的最快的结果如下: def bitsToBytes(a): s = i = 0 for x in a: s += s + x i +
def bitsToBytes(a):
s = i = 0
for x in a:
s += s + x
i += 1
if i == 8:
yield s
s = i = 0
if i > 0:
yield s << (8 - i)
这里我用列表来展示示例。发电机可以。但是,字符串不会,因此有必要在类似列表的数据和字符串之间来回转换。在8-er块中使用位并忽略异常的最简单策略:
def getbytes(bits):
done = False
while not done:
byte = 0
for _ in range(0, 8):
try:
bit = next(bits)
except StopIteration:
bit = 0
done = True
byte = (byte << 1) | bit
yield byte
getbytes
是一个生成器,它接受一个生成器,也就是说,它可以很好地处理大型且可能无限的流。步骤1:添加缓冲区零
步骤2:反转位,因为你的尾数是反转的
步骤3:连接成单个字符串
步骤4:一次将8位保存到数组中
第五步:
第六步:利润
def bitsToBytes(a):
a = [0] * (8 - len(a) % 8) + a # adding in extra 0 values to make a multiple of 8 bits
s = ''.join(str(x) for x in a)[::-1] # reverses and joins all bits
returnInts = []
for i in range(0,len(s),8):
returnInts.append(int(s[i:i+8],2)) # goes 8 bits at a time to save as ints
return returnInts
使用:
如果输入是字符串,则专用解决方案可能更快:
>>> bits = '100000001'
>>> padded_bits = bits + '0' * (8 - len(bits) % 8)
>>> padded_bits
'1000000010000000'
>>> list(int(padded_bits, 2).to_bytes(len(padded_bits) // 8, 'big'))
[128, 128]
如果
len(bits)%8==0
@adam,那么最后一个字节是零。如果你是对的,我的意思是list,尽管我认为通常的措辞是“bit string”(可能我错了!)。我更新了问题以澄清这一点。为什么[1,1]输出[192]而不是[3]和[1,0,0,0,0,0,0,0,1]输出[128,128]而不是[1,1]?@CodeMonkey我将字节从高阶位填充到低阶位,并在最后一个字节中添加填充零(因此在低阶位中添加)。因此,一个1单独被理解为字节0b10000000,两个连续的1被理解为字节0b11000000,[1,0,0,0,0,0,1]被理解为字节[0b10000000,0b1000000]。顺便说一下,这与中的位顺序相同。为什么bytestring不是一个可接受的结果?在Python3中,枚举BYTestString(如b'\x80\x80'
会产生整数,即bytes
对象是一个字节序列(Pythonint
在范围(0x100)
中)。为了获得最佳性能,可以在Cython中编写一个C扩展名:a2b\u-bin():b'1'->b'\x80'
类似于感谢这个非常有趣的“grouper”,以及我不知道的int.to_字节!事实上,你的第一个解决方案在这里稍微慢一点,但你的第二个解决方案比我的解决方案快30%以上,只有一行。您在上面的评论中是对的,返回一个字节字符串是可以的,它们很容易在以后的文件io中使用。使用一个列表作为输入,我这样做int(“.”join(“01”[x]表示数据中的x)+“0”*k,2).to_字节(n,“big”)
其中k和n的计算与您的示例相同。使用int(“.”join(map(“01”)。\uu getitem_u,data))+“0”*k,2).to_字节(n,“big”)
@Jean-ClaudeArbaut:你可以发布一个带有时间比较和''的答案。加入基于的解决方案。注意:grouper()
基于的解决方案可以将无限位流转换为无限字节流(只需将[]
替换为()
即可将列表转换为生成器)——其目的是接受任意位的“集合”。对于不同类型的输入(位序列、iterable、bytestring)和输入大小(小、大),不同的解决方案可能更快。
def bitsToBytes(a):
a = [0] * (8 - len(a) % 8) + a # adding in extra 0 values to make a multiple of 8 bits
s = ''.join(str(x) for x in a)[::-1] # reverses and joins all bits
returnInts = []
for i in range(0,len(s),8):
returnInts.append(int(s[i:i+8],2)) # goes 8 bits at a time to save as ints
return returnInts
from functools import reduce
from itertools import zip_longest
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return zip_longest(*args, fillvalue=fillvalue)
bytes = [reduce(lambda byte, bit: byte << 1 | bit, eight_bits)
for eight_bits in grouper(bits, 8, fillvalue=0)]
[] -> []
[1] -> [128]
[1, 1] -> [192]
[1, 0, 0, 0, 0, 0, 0, 0, 1] -> [128, 128]
>>> bits = '100000001'
>>> padded_bits = bits + '0' * (8 - len(bits) % 8)
>>> padded_bits
'1000000010000000'
>>> list(int(padded_bits, 2).to_bytes(len(padded_bits) // 8, 'big'))
[128, 128]