关于struct:如何在Python中将长整数写为二进制?

How do I write a long integer as binary in Python?

在python中,长整数具有无限的精度。我想写一个16字节(128位)的整数到一个文件。标准库中的struct只支持最多8个字节的整数。array具有相同的限制。有没有一种方法可以做到这一点而不屏蔽和移动每个整数?

这里有一些说明:我正在写一个将从非python程序中读取的文件,所以pickle已经不在了。所有128位都被使用。


我认为对于无符号整数(忽略了endianness),类似于

1
2
3
4
5
6
7
8
9
10
11
12
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的输出、不使用显式掩码和(我假设)不使用任何非标准模块的要求。不过不是特别优雅。


两种可能的解决方案:

  • 只需泡菜你的长整数。这将以一种特殊的格式写入整数,如果这是您所希望的,则可以再次读取它。

  • 使用此答案中的第二个代码段将long int转换为big endian字符串(如果愿意,可以很容易地将其更改为little endian),然后将此字符串写入文件。

  • 问题是bigints的内部表示不直接包括您请求的二进制数据。


    pypi位数组模块与内置的bin()函数相结合,对于一个简单而灵活的解决方案来说,似乎是一个很好的组合。

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

    可以用更多的代码行来控制endianness。你得评估一下效率。


    为什么不将struct与无符号长整型一起使用两次?

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

    这里记录了这一点(向下滚动以获得带有q的表):http://docs.python.org/library/struct.html


    使用python 3.2及更高版本,您可以使用int.to_bytesint.from_bytes:https://docs.python.org/3/library/stdtypes.html int.to-u字节


    这可能无法避免"屏蔽并移动每个整数"的要求。我不确定在python long值的上下文中,避免使用mask和shift意味着什么。

    字节如下:

    1
    2
    3
    4
    5
    6
    7
    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 )打包这个字节列表。


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

    1
    2
    3
    4
    5
    6
    bigint = 0xFEDCBA9876543210FEDCBA9876543210L
    print bigint,hex(bigint).upper()

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

    print len(cbi)

    bigint本身被拒绝,但是如果您用&0xffffffffffffffffffffffff屏蔽它,您可以将它减少到8字节int而不是16字节。然后上半部分也被移动和遮蔽。您可能需要对字节排序进行一些操作。我用过了!标记以告诉它生成网络结束字节顺序。此外,可能需要反转MSB和LSB(上下字节)。我将把它作为练习留给用户来决定。我想说,将数据保存为network endian会更安全,所以您总是知道数据的endian是什么。

    不,不要问我network endian是大还是小…


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

    但是写一个通过移动来转储16字节整数的函数,如果它不是时间临界的,就不那么难了。