3.5 å—节到大整数的打包与解包¶
问题¶
ä½ æœ‰ä¸€ä¸ªå—节å—符串并想将它解压æˆ�ä¸€ä¸ªæ•´æ•°ã€‚æˆ–è€…ï¼Œä½ éœ€è¦�将一个大整数转æ�¢ä¸ºä¸€ä¸ªå—节å—符串。
解决方案¶
å�‡è®¾ä½ 的程åº�需è¦�处ç�†ä¸€ä¸ªæ‹¥æœ‰128ä½�é•¿çš„16ä¸ªå…ƒç´ çš„å—节å—符串。比如:
data = b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
为了将bytesè§£æž�为整数,使用 int.from_bytes() 方法,并åƒ�下é�¢è¿™æ ·æŒ‡å®šå—节顺åº�:
>>> len(data)
16
>>> int.from_bytes(data, 'little')
69120565665751139577663547927094891008
>>> int.from_bytes(data, 'big')
94522842520747284487117727783387188
>>>
为了将一个大整数转æ�¢ä¸ºä¸€ä¸ªå—节å—符串,使用 int.to_bytes() 方法,并åƒ�下é�¢è¿™æ ·æŒ‡å®šå—节数和å—节顺åº�:
>>> 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 模å�—æ�¥è§£åŽ‹å¯¹äºŽæ•´æ•°çš„å¤§å°�是有é™�制的。
å› æ¤ï¼Œä½ å�¯èƒ½æƒ³è§£åŽ‹å¤šä¸ªå—节串并将结果å�ˆå¹¶ä¸ºæœ€ç»ˆçš„结果,就åƒ�下é�¢è¿™æ ·ï¼š
>>> 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进制数的表示ä¸å�¯ä»¥å¾ˆå®¹æ˜“的看出æ�¥ï¼š
>>> 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() 方法æ�¥å†³å®šéœ€è¦�多少å—节ä½�æ�¥å˜å‚¨è¿™ä¸ªå€¼ã€‚
>>> 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'
>>>