在 python 中将 64 位整数转换为 8 个单独的 1 字节整数
Convert a 64 bit integer into 8 separate 1 byte integers in python
在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
和 return 设为一个长度为 8 的数组,其中数组中的每个整数都是上面列出的整数。
我打算使用 struct
,但老实说,阅读 documentation 并没有完全清楚我将如何实现它。
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 的位移几乎没有什么优势,我更倾向于使用你和其他人认为更易读的任何东西。
解决方案
不将数字转换为字符串的解决方案:
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
,我select这个数字的最后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 提议)似乎是最快的代码段。
这是一个使用 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
在我的电脑上,这比字节移位解决方案快三倍。
在Python2.x,struct.pack
returns一串字节。很容易将其转换为整数数组。
>>> 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'
意味着将给定的数据打包为 big-endian unsigned long long
。要以相反的顺序获取字节,您可以使用 <
代替 little-endian。
最后的操作是一个列表理解,它遍历字节字符串的字符并使用 ord
内置函数来获取该字符的整数表示。
最后说明:Python 实际上没有整数大小的概念。在2.x中,有限制为32位的int
,还有不限大小的long
。在 3.x 中,这两个被统一为一个类型。因此,即使此操作保证给出仅占用一个字节的整数,但如果您在其他操作中使用它们,请注意 python 将强制结果整数保持这种状态。
这对于一堆 unit64 来说似乎更快。使用 numpy.
from cytpes import *
import numpy as np
l1 = c_uint64 * 512
payload64 = l1(0)
payload8 = np.frombuffer(payload64, dtype=np.uint8)
其中 payload8 是 np.unit8 的数组,其大小是 payload64 的 8 倍,并且其中包含转换后的字节。
对我来说,它比结构变体更快...
for i in range(len(payload64)):
payload8[i*8:i*8+8] = struct.unpack('8B', struct.pack('Q', payload64[i]))
在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
和 return 设为一个长度为 8 的数组,其中数组中的每个整数都是上面列出的整数。
我打算使用 struct
,但老实说,阅读 documentation 并没有完全清楚我将如何实现它。
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 的位移几乎没有什么优势,我更倾向于使用你和其他人认为更易读的任何东西。
解决方案
不将数字转换为字符串的解决方案:
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
,我select这个数字的最后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 提议)似乎是最快的代码段。
这是一个使用 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
在我的电脑上,这比字节移位解决方案快三倍。
在Python2.x,struct.pack
returns一串字节。很容易将其转换为整数数组。
>>> 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'
意味着将给定的数据打包为 big-endian unsigned long long
。要以相反的顺序获取字节,您可以使用 <
代替 little-endian。
最后的操作是一个列表理解,它遍历字节字符串的字符并使用 ord
内置函数来获取该字符的整数表示。
最后说明:Python 实际上没有整数大小的概念。在2.x中,有限制为32位的int
,还有不限大小的long
。在 3.x 中,这两个被统一为一个类型。因此,即使此操作保证给出仅占用一个字节的整数,但如果您在其他操作中使用它们,请注意 python 将强制结果整数保持这种状态。
这对于一堆 unit64 来说似乎更快。使用 numpy.
from cytpes import *
import numpy as np
l1 = c_uint64 * 512
payload64 = l1(0)
payload8 = np.frombuffer(payload64, dtype=np.uint8)
其中 payload8 是 np.unit8 的数组,其大小是 payload64 的 8 倍,并且其中包含转换后的字节。
对我来说,它比结构变体更快...
for i in range(len(payload64)):
payload8[i*8:i*8+8] = struct.unpack('8B', struct.pack('Q', payload64[i]))