如何在Python中将UUID打包到结构体中?

10 投票
3 回答
7585 浏览
提问于 2025-04-16 22:32

我有一个UUID(通用唯一识别码),我想把它放进一个结构体里,用UUID.int把它变成一个128位的整数。但是,结构体的格式字符没有一个能存下这么大的数,我该怎么做呢?

示例代码:

s = struct.Struct('L')
unique_id = uuid.uuid4()    
tuple = (unique_id.int)
packed = s.pack(*tuple)

问题是,结构体格式'L'只有4个字节……我需要存16个字节。把它存成一个32个字符的字符串又有点多。

3 个回答

1

简要说明

struct.pack('<QBBHHL', *uuid_foo.fields[::-1])

介绍

虽然Cat++的回答很不错,但它把UUID分成了两半,放进了两个无符号长整型里。我想要把每个字段打包,所以我得到了以下内容:

def maxsize(size: typing.Union[int,str]):
    """ Useful for playing with different struct.pack formats """
    if isinstance(size, str):
        size = struct.calcsize(size)
    return 2 ** (4 * size) - 1

uuid_max = uuid.UUID('ffffffff-ffff-ffff-ffff-ffffffffffff')
tuple(maxsize(len(f)) for f in str(u).split('-'))
# (4294967295, 65535, 65535, 65535, 281474976710655)

uuid_max.fields
# (4294967295, 65535, 65535, 255, 255, 281474976710655)

uuid_foo = UUID('909822be-c5c4-432f-95db-da1be79cf067')
uuid_foo.fields
# (2425889470, 50628, 17199, 149, 219, 239813384794215)

前五个字段比较简单,因为它们的大小分别是无符号的8、4、4、2、2位整数,正好可以对应上。最后一个字段则需要借助另一个答案的帮助

注意:填充只会在连续的结构成员之间自动添加。编码结构的开头和结尾不会添加填充。

使用非本地大小和对齐时,例如使用‘<’,‘>’,‘=’,和‘!’,也不会添加填充。

为了将结构的末尾对齐到特定类型的对齐要求,格式的最后要加上该类型的代码,并且重复次数为零。请参见示例。

struct.pack('>LHHBBQ', *uuid_foo.fields)
# b'\x90\x98"\xbe\xc5\xc4C/\x95\xdb\x00\x00\xda\x1b\xe7\x9c\xf0g'
#                                    ^^  ^^ these empty bytes won't work!

实际答案

由于最后一个字段的大小是12,所以你需要反向打包和解包,也就是小端模式。这样的话,最后会留下零,而不是在第五个和第六个字段之间。

struct.unpack('<QBBHHL', struct.pack('<QBBHHL', *uuid_foo.fields[::-1]))
# (281474976710655, 255, 255, 65535, 65535, 4294967295)

uuid_foo.fields
# (4294967295, 65535, 65535, 255, 255, 281474976710655)

重新生成这个需要你再反转一次。

uuid_packed = struct.pack('<QBBHHL', *uuid_foo.fields[::-1])
uuid_unpacked = struct.unpack('<QBBHHL', uuid_packed)[::-1]
uuid.UUID(fields=uuid_unpacked)
# UUID('909822be-c5c4-432f-95db-da1be79cf067')
8

你在使用 uuid 模块时,可以直接使用 bytes 这个部分。它保存了一个 UUID,作为一个16字节的字符串(里面包含六个整数字段,按照大端字节顺序排列)

u = uuid.uuid4()
packed = u.bytes # packed is a string of size 16
assert u == uuid.UUID(bytes=packed)
18

这是一个128位的整数,你觉得它会变成什么呢?你可以把它分成几个部分,比如说两个64位的整数:

max_int64 = 0xFFFFFFFFFFFFFFFF
packed    = struct.pack('>QQ', (u.int >> 64) & max_int64, u.int & max_int64)
# unpack
a, b     = struct.unpack('>QQ', packed)
unpacked = (a << 64) | b

assert u.int == unpacked

撰写回答