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

10

我有一个UUID,想使用UUID.int将其打包到struct中,这将使它变成一个128位的整数。但是,没有一个struct格式字符足够大可以存储它,该怎么办?

示例代码:

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

问题是,结构化格式“L”只有4个字节...我需要存储16个字节。将其存储为32个字符的字符串有点过多。

请发布您的代码示例。 - Adam Matan
3个回答

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

运行得非常好。我曾经想过可能有更简单的方法,比如使用UUID.bytes,但是位移已经足够简单了。 - nicobatu

8

因为您在使用 uuid 模块,所以您可以简单地使用 bytes 成员,它保存了UUID作为一个16字节的字符串(包含六个整数字段,按大端字节顺序排列)

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

但是由于结构体只能存储一个字符,我必须将字节迭代为16个单独的字符。然后在解包时,再次组合16个字符。编辑:看起来它也可以存储字符串,我会尝试一下。 - nicobatu
我正在使用的Python版本似乎在pack中对字符串进行了一些隐式转换,我认为使用long更安全。 - nicobatu
1
我看不出有什么问题,你应该可以使用stuct.pack("16s", packed),它等同于struct.pack("16c", *[c for c in packed])(并且只是重新复制了你的字符串)。 - tomasz

1

简而言之

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

介绍

虽然Cat++的答案非常好,但它将UUID分成两个unsigned long longs来适应它。我想要打包每个字段,这让我得到了以下结果:

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')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接