如何在Python中将长整数写为二进制?

时间:2011-01-12 15:13:38

标签: python struct biginteger

在Python中,长整数具有无限精度。我想写一个16字节(128位)整数到一个文件。标准库中的struct仅支持最多8个字节的整数。 array具有相同的限制。有没有办法在没有屏蔽和移动每个整数的情况下做到这一点?

这里有一些澄清:我正在写一个将从非Python程序读入的文件,因此pickle已经出局了。使用全部128位。

9 个答案:

答案 0 :(得分:8)

我认为对于无符号整数(并忽略字节序),如

import binascii

def binify(x):
    h = hex(x)[2:].rstrip('L')
    return binascii.unhexlify('0'*(32-len(h))+h)

>>> for i in 0, 1, 2**128-1:
...     print i, repr(binify(i))
... 
0 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
1 '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'
340282366920938463463374607431768211455 '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'

可能在技术上满足具有非Python特定输出的要求,而不是使用显式掩码,并且(我假设)不使用任何非标准模块。但不是特别优雅。

答案 1 :(得分:5)

两种可能的解决方案:

  1. 只需pickle您的长整数。这将以特殊格式写入整数,这样就可以再次读取它,如果你想要的话。

  2. 使用this answer中的第二个代码片段将long int转换为大端字符串(如果您愿意,可以将其轻松更改为小端),并将此字符串写入您的文件。 / p>

  3. 问题是bigints的内部表示不直接包含您要求的二进制数据。

答案 2 :(得分:3)

PyPi bitarray模块与内置bin()功能相结合,似乎是一个简单灵活的解决方案的良好组合。

bytes = bitarray(bin(my_long)[2:]).tobytes()

可以使用更多代码行来控制字节序。你必须评估效率。

答案 3 :(得分:2)

为什么不两次使用 unsigned long long 类型的struct?

import struct
some_file.write(struct.pack("QQ", var/(2**64), var%(2**64)))

这里有记录(向下滚动以获得Q表):http://docs.python.org/library/struct.html

答案 4 :(得分:1)

这可能无法避免“屏蔽并移动每个整数”的要求。我不确定在Python长值的上下文中避免使用掩码和移位意味着什么。

字节是这些:

def bytes( long_int ):
    bytes = []
    while long_int != 0:
        b = long_int%256
        bytes.insert( 0, b )
        long_int //= 256
    return bytes

然后,您可以使用struct.pack( '16b', bytes )

打包此字节列表

答案 5 :(得分:1)

使用Python 3.2及更高版本,您可以使用int.to_bytesint.from_byteshttps://docs.python.org/3/library/stdtypes.html#int.to_bytes

答案 6 :(得分:0)

您可以将对象pickle为二进制,使用协议缓冲区(我不知道它们是否允许您序列化无限精度整数)或BSON(如果您不想编写代码)。

但是,如果它不是时间关键的话,编写一个通过移位来转储16字节整数的函数应该不那么难。

答案 7 :(得分:0)

这可能有点晚了,但我不明白为什么你不能使用struct:

bigint = 0xFEDCBA9876543210FEDCBA9876543210L
print bigint,hex(bigint).upper()

cbi = struct.pack("!QQ",bigint&0xFFFFFFFFFFFFFFFF,(bigint>>64)&0xFFFFFFFFFFFFFFFF)

print len(cbi)

bigint本身被拒绝,但是如果用& 0xFFFFFFFFFFFFFFFF掩盖它,你可以将它减少到8字节而不是16字节。然后上部移位并屏蔽。您可能需要稍微使用字节排序。我用了!标记告诉它产生网络字节序字节顺序。此外,可能需要反转msb和lsb(上部和下部字节)。我将把它作为练习让用户确定。我会说保存东西,因为网络端将更安全,所以你总是知道你的数据的结束是什么。

不,不要问我网络端是大还是小端......

答案 8 :(得分:0)

基于@DSM的答案,并且为了支持负整数和变化的字节大小,我创建了以下改进的代码段:

def to_bytes(num, size):
        x = num if num >= 0 else 256**size + num
        h = hex(x)[2:].rstrip("L")
        return binascii.unhexlify("0"*((2*size)-len(h))+h)

这将正确处理负整数,并让用户设置字节数