struct.pack()函数允许将最多64位的整数转换为字节串。打包更大整数的最有效方法是什么?我宁愿不添加像PyCrypto这样的非标准模块的依赖(它提供了num_to_bytes())。
答案 0 :(得分:5)
你是说某事是这样的:
def num_to_bytes(num):
bytes = []
num = abs(num) # Because I am unsure about negatives...
while num > 0:
bytes.append(chr(num % 256))
num >>= 8
return ''.join(reversed(bytes))
def bytes_to_num(bytes):
num = 0
for byte in bytes:
num <<= 8
num += ord(byte)
return num
for n in (1, 16, 256, 257, 1234567890987654321):
print n,
print num_to_bytes(n).encode('hex'),
print bytes_to_num(num_to_bytes(n))
返回:
1 01 1
16 10 16
256 0100 256
257 0101 257
1234567890987654321 112210f4b16c1cb1 1234567890987654321
我只是不确定如何处理否定因素......我对那些不熟悉的事情并不熟悉。
编辑:另一种解决方案(我的测试速度提高约30%):
def num_to_bytes(num):
num = hex(num)[2:].rstrip('L')
if len(num) % 2:
return ('0%s' % num).decode('hex')
return num.decode('hex')
def bytes_to_num(bytes):
return int(bytes.encode('hex'), 16)
答案 1 :(得分:4)
遇到同一问题。从python 3.2开始,您可以使用int.to_bytes
:
>>> (2**100).to_bytes(16, byteorder='big')
b'\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
答案 2 :(得分:3)
假设海报想要将一个大整数打包为二进制字符串,即不在数字中每个数字使用一个字节的存储空间。这样做的一种方法似乎是:
import marshal
a = 47L
print marshal.dumps(a)
打印:
'l\x01\x00\x00\x00/\x00'
我不能说我理解如何解释这些位,现在......
答案 3 :(得分:2)
我认为你的意思是你只想使用尽可能多的字节代表数字?例如如果数字是:
在Psion PDA上,他们通常会有一些打包方案,其中您读取第一个字节,检测它是否具有最高位设置,然后读取另一个字节(如果有)。这样你就可以继续读取字节,直到你读到“完整”数字。如果您处理的大多数数字相当小,那么该系统运行良好,因为您通常每个数字只使用一个或两个字节。
另一种方法是让一个(或多个)字节表示所使用的总字节数,但此时它基本上是Python中的一个字符串。即它是一串256位数字。
答案 4 :(得分:2)
这有点hacky,但你可以通过十六进制字符串表示,并使用十六进制编解码器二进制文件:
>>> a = 2**60
>>> a
1152921504606846976L
>>> hex(a)
'0x1000000000000000L'
>>> hex(a).rstrip("L")[2:].decode('hex')
'\x10\x00\x00\x00\x00\x00\x00\x00' # 8bytes, as expected.
>>> int(_.encode('hex'), 16)
1152921504606846976L
它打破了一点因为十六进制编解码器需要偶数个数字,所以你需要填充它,你需要设置一个标志来处理负数。这是一个通用包/解包:
def pack(num):
if num <0:
num = (abs(num) << 1) | 1 # Hack - encode sign as lowest bit.
else:
num = num << 1
hexval = hex(num).rstrip("L")[2:]
if len(hexval)%2 ==1: hexval = '0' + hexval
return hexval.decode('hex')
def unpack(s):
val = int(s.encode('hex'), 16)
sign = -1 if (val & 1) else 1
return sign * (val>>1)
for i in [10,4534,23467, 93485093485, 2**50, 2**60-1, -1, -20, -2**60]:
assert unpack(pack(i)) == i
虽然需要填充所有的填充物,但我不确定它是否比手动解决方案好得多。
答案 5 :(得分:0)
正如S.Lott在评论中所建议的那样,只需将数字转换为字符串并打包该字符串即可。例如,
x = 2 ** 12345
struct.pack("40s", str(x))
答案 6 :(得分:0)
这概括了前面的answer by Claudio。它尝试使用最佳字节长度。它还支持无符号和有符号整数。
def int_to_bytes(i: int, *, signed: bool = False) -> bytes:
length = (i.bit_length() + 7 + signed) // 8
return i.to_bytes(length, byteorder='big', signed=signed)
def bytes_to_int(b: bytes, *, signed: bool = False) -> int:
return int.from_bytes(b, byteorder='big', signed=signed)
# Test unsigned:
for i in range(1025):
assert i == bytes_to_int(int_to_bytes(i))
# Test signed:
for i in range(-1024, 1025):
assert i == bytes_to_int(int_to_bytes(i, signed=True), signed=True)