Files
python3-cookbook/source/c03/p05_pack_unpack_large_int_from_bytes.rst
2015-12-28 19:34:04 +08:00

99 lines
3.1 KiB
ReStructuredText
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

==========================
3.5 字节到大整数的打包与解包
==========================
----------
问题
----------
你有一个字节字符串并想将它解压成一个整数。或者,你需要将一个大整数转换为一个字节字符串。
----------
解决方案
----------
假设你的程序需要处理一个拥有128位长的16个元素的字节字符串。比如
.. code-block:: python
data = b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
为了将bytes解析为整数使用 ``int.from_bytes()`` 方法,并像下面这样指定字节顺序:
.. code-block:: python
>>> len(data)
16
>>> int.from_bytes(data, 'little')
69120565665751139577663547927094891008
>>> int.from_bytes(data, 'big')
94522842520747284487117727783387188
>>>
为了将一个大整数转换为一个字节字符串,使用 ``int.to_bytes()`` 方法,并像下面这样指定字节数和字节顺序:
.. code-block:: python
>>> x = 94522842520747284487117727783387188
>>> x.to_bytes(16, 'big')
b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
>>> x.to_bytes(16, 'little')
b'4\x00#\x00\x01\xef\xcd\x00\xab\x90x\x00V4\x12\x00'
>>>
----------
讨论
----------
大整数和字节字符串之间的转换操作并不常见。
然而,在一些应用领域有时候也会出现,比如密码学或者网络。
例如IPv6网络地址使用一个128位的整数表示。
如果你要从一个数据记录中提取这样的值的时候,你就会面对这样的问题。
作为一种替代方案你可能想使用6.11小节中所介绍的 ``struct`` 模块来解压字节。
这样也行得通,不过利用 ``struct`` 模块来解压对于整数的大小是有限制的。
因此,你可能想解压多个字节串并将结果合并为最终的结果,就像下面这样:
.. code-block:: python
>>> data
b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
>>> import struct
>>> hi, lo = struct.unpack('>QQ', data)
>>> (hi << 64) + lo
94522842520747284487117727783387188
>>>
字节顺序规则(little或big)仅仅指定了构建整数时的字节的低位高位排列方式。
我们从下面精心构造的16进制数的表示中可以很容易的看出来
.. code-block:: python
>>> x = 0x01020304
>>> x.to_bytes(4, 'big')
b'\x01\x02\x03\x04'
>>> x.to_bytes(4, 'little')
b'\x04\x03\x02\x01'
>>>
如果你试着将一个整数打包为字节字符串,那么它就不合适了,你会得到一个错误。
如果需要的话,你可以使用 ``int.bit_length()`` 方法来决定需要多少字节位来存储这个值。
.. code-block:: python
>>> x = 523 ** 23
>>> x
335381300113661875107536852714019056160355655333978849017944067
>>> x.to_bytes(16, 'little')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too big to convert
>>> x.bit_length()
208
>>> nbytes, rem = divmod(x.bit_length(), 8)
>>> if rem:
... nbytes += 1
...
>>>
>>> x.to_bytes(nbytes, 'little')
b'\x03X\xf1\x82iT\x96\xac\xc7c\x16\xf3\xb9\xcf...\xd0'
>>>