将python中的64位整数转换为8个单独的1字节整数

问题描述 投票:12回答:5

在python中,我得到了一个64位整数。该整数是通过采用几个不同的8位整数并将它们混搭为一个64位巨型整数而创建的。再次将它们分开是我的工作。

例如:

Source number: 2592701575664680400
Binary (64 bits): 0010001111111011001000000101100010101010000101101011111000000000
int 1: 00100011 (35)
int 2: 11111011 (251)
int 3: 00100000 (32)
int 4: 01011000 (88)
int 5: 10101010 (170)
int 6: 00010110 (22)
int 7: 10111110 (190)
int 8: 00000000 (0)

所以我想做的是获取源编号2592701575664680373,并返回一个长度为8的数组,其中数组中的每个int都是上面列出的int。

[我本打算使用struct,但说实话,阅读documentation并不清楚我将如何实现。

python bitmask
5个回答
7
投票

在Python 2.x中,struct.pack返回一个字节字符串。将其转换为整数数组很容易。

>>> bytestr = struct.pack('>Q', 2592701575664680400)
>>> bytestr
'#\xfb X\xaa\x16\xbd\xd0'
>>> [ord(b) for b in bytestr]
[35, 251, 32, 88, 170, 22, 189, 208]

python中的struct模块用于将python对象转换为字节字符串,通常根据C结构打包规则进行打包。 struct.pack接受格式说明符(描述结构字节的布局方式的字符串)和一些python数据,并将其打包为字节字符串。 struct.unpack进行相反操作,采用格式说明符和字节字符串,并再次以python对象的格式返回未打包数据的元组。

正在使用的格式说明符分为两部分。前导字符指定字符串的字节序(字节顺序)。以下字符指定要打包或解压缩的结构的字段的类型。因此,'>Q'意味着将给定的数据打包为高位优先级unsigned long long。要获取相反顺序的字节,可以使用<代替little-endian。

最终操作是列表理解,它遍历字节字符串的字符并使用ord内置函数来获取该字符的整数表示。

最后说明:Python实际上没有整数大小的概念。在2.x中,int限制为32位,而long的大小不受限制。在3.x中,这两个被统一为一个类型。因此,即使此操作保证提供仅占用一个字节的整数,但如果在其他操作中使用python,则注意python仍将迫使所得整数保持这种方式。


9
投票

解决方案

无需将数字转换为字符串的解决方案:

x = 0b0010001111111011001000000101100010101010000101101011111000000000

numbers = list((x >> i) & 0xFF for i in range(0,64,8))
print(numbers)                    # [0, 190, 22, 170, 88, 32, 251, 35]
print(list(reversed(numbers)))    # [35, 251, 32, 88, 170, 22, 190, 0]

说明

这里,我使用列表推导,在i上以8为增量进行循环。因此i取值为0, 8, 16, 24, 32, 40, 48, 56。每次,位移位运算符>>都会将数字x暂时下移i位。这等效于除以256^i

因此,结果数为:

i = 0:   0010001111111011001000000101100010101010000101101011111000000000
i = 8:           00100011111110110010000001011000101010100001011010111110
i = 16:                  001000111111101100100000010110001010101000010110
i = 24:                          0010001111111011001000000101100010101010
i = 32:                                  00100011111110110010000001011000
i = 40:                                          001000111111101100100000
i = 48:                                                  0010001111111011
i = 56:                                                          00100011

通过使用& 0xFF,我选择此数字的后8位。示例:

x >> 48:           001000111111101100100000
0xff:                              11111111
(x >> 48) & 0xff:  000000000000000000100000

由于前导零并不重要,所以您具有所需的数字。

结果将转换为列表并以正常和相反的顺序打印(就像OP想要的那样。

性能

我将此结果的时间与该线程中提出的其他解决方案进行了比较:

In: timeit list(reversed([(x >> i) & 0xFF for i in range(0,64,8)]))
100000 loops, best of 3: 13.9 µs per loop

In: timeit [(x >> (i * 8)) & 0xFF for i in range(7, -1, -1)]
100000 loops, best of 3: 11.1 µs per loop

In: timeit [(x >> i) & 0xFF for i in range(63,-1,-8)]
100000 loops, best of 3: 10.2 µs per loop

In: timeit reversed(struct.unpack('8B', struct.pack('Q', x)))
100000 loops, best of 3: 3.22 µs per loop

In: timeit reversed(struct.pack('Q', x))
100000 loops, best of 3: 2.07 µs per loop

结果:我的解决方案是不是最快的!当前,直接使用struct(由Mark Ransom提出)似乎是最快的代码段。


2
投票
bn = "0010001111111011001000000101100010101010000101101011111000000000"

print([int(bn[i:i+8], 2) for i in range(0,len(bn), 8)])
[35, 251, 32, 88, 170, 22, 190, 0]

如果使用n的二进制表示,则输出将不同:

n = 2592701575664680373
bn = bin(n)

print([int(bn[i:i+8], 2) for i in range(0,len(bn), 8)])
[35, 251, 32, 88, 170, 22, 189, 181]

某些时间:

In [16]: %%timeit                                                
numbers = list((n >> i) & 0xFF for i in range(0,64,8))
list(reversed(numbers))
   ....: 
100000 loops, best of 3: 2.97 µs per loop

In [17]: timeit [(n >> (i * 8)) & 0xFF for i in range(7, -1, -1)]
1000000 loops, best of 3: 1.73 µs per loop

In [18]: %%timeit                                                
bn = bin(n)
[int(bn[i:i+8], 2) for i in range(0,len(bn), 8)]
   ....: 
100000 loops, best of 3: 3.96 µs per loop

您也可以只使用divmod:

out = []
for _ in range(8):
    n, i = divmod(n, 256)
    out.append(i) 
out = out[::-1]

效率几乎相同:

In [31]: %%timeit
   ....: n = 2592701575664680411
   ....: out = []
   ....: for _ in range(8):
   ....:     n, i = divmod(n, 1 << 8)
   ....:     out.append(i)
   ....: out[::-1]
   ....: 
100000 loops, best of 3: 2.35 µs per loop

使用python进行位转换几乎没有优势,我将更倾向于使用您和其他人觉得更易读的内容。


2
投票

这里是使用struct的版本:

import struct
n = 2592701575664680400
bytes = struct.unpack('8B', struct.pack('Q', n))

bytes的返回顺序与您在问题中显示的顺序相反。

以下是性能统计:

python -m timeit -s "import struct" "struct.unpack('8B', struct.pack('Q', 2592701575664680400))"
1000000 loops, best of 3: 0.33 usec per loop

在我的计算机上,这比字节移位解决方案快三倍。


0
投票

对于一堆unit64来说,这似乎更快。使用numpy。

from cytpes import *
import numpy as np
l1 = c_uint64 * 512
payload64 = l1(0)
payload8 = np.frombuffer(payload64, dtype=np.uint8)

其中有效负载8是np.unit8的数组,其后是有效负载64大小的8倍,并且其中包含转换后的字节。

对我来说,它比struct变体要快...

for i in range(len(payload64)):
       payload8[i*8:i*8+8] = struct.unpack('8B', struct.pack('Q', payload64[i]))
© www.soinside.com 2019 - 2024. All rights reserved.