将 UUID 32 个字符的十六进制字符串转换为“YouTube 风格”的短 id 并返回

Posted

技术标签:

【中文标题】将 UUID 32 个字符的十六进制字符串转换为“YouTube 风格”的短 id 并返回【英文标题】:Convert UUID 32-character hex string into a "YouTube-style" short id and back 【发布时间】:2012-08-29 13:09:13 【问题描述】:

我正在使用 uuid.uuid1() 为我的所有 MongoDB 文档分配一个 GUID。我想要一种可以派生 11 个字符、唯一、区分大小写的类似 YouTube 的 ID 的方法,例如

1_XmY09uRJ4 

来自 uuid 生成的十六进制字符串,看起来像

ae0a0c98-f1e5-11e1-9t2b-1231381dac60

我希望能够动态地将缩短的 ID 与十六进制匹配,反之亦然,无需在数据库中存储另一个字符串。有没有人有一些示例代码或者可以指出可以做到这一点的模块或公式的方向?

【问题讨论】:

什么“t”?我不确定你指的是什么 @yourfriendzak:您的 UUID 包含“t”,使其无效。 嗯,这很奇怪...我直接从 uuid.hex 输出复制并粘贴它... AFAIR,UUID 有一个时间组件。您的字符串可能使用 't' 作为分隔符。 @Carlos UUID 永远不会显示为带有时间分量的十六进制。 t 打破了第 9 个字节的十六进制表示(共 16 个);它通常是一个十六进制数字。我的钱在 f 上,而 OP 将其误读为 t,或者他们在某个时间点按下了 t 键并选择了该数字。无论如何,Python 的 uuid.uuid1() 只会生成一个 uuid.UUID() 实例,它的 hex 属性输出值的 8-4-4-4-12 十六进制数字表示,并且将从不包含一个t。这里,t 是时钟序列值的 14 位的一部分(从右到左计数时为 9-12 位)。 【参考方案1】:

将底层字节转换为 base64 值,去除 = 填充和换行符。

您可能希望使用base64.urlsafe_b64encode() function 来避免使用/+(使用_-),因此生成的字符串可以用作URL 路径元素:

>>> import uuid, base64
>>> base64.urlsafe_b64encode(uuid.uuid1().bytes).rstrip(b'=').decode('ascii')
'81CMD_bOEeGbPwAjMtYnhg'

反过来:

>>> uuid.UUID(bytes=base64.urlsafe_b64decode('81CMD_bOEeGbPwAjMtYnhg' + '=='))
UUID('f3508c0f-f6ce-11e1-9b3f-002332d62786')

将其转换为通用函数:

from base64 import urlsafe_b64decode, urlsafe_b64encode
from uuid import UUID

def uuid2slug(uuidstring):
    return urlsafe_b64encode(UUID(uuidstring).bytes).rstrip(b'=').decode('ascii')

def slug2uuid(slug):
    return str(UUID(bytes=urlsafe_b64decode(slug + '==')))

这为您提供了一种以更紧凑的形式表示 16 字节 UUID 的方法。进一步压缩会丢失信息,这意味着您无法再次将其解压缩为完整的 UUID。 16 个字节可以表示的所有值都不会小于 22 个 base64 字符,每三个字节的输入需要 4 个字符,每个字符编码 6 位信息。

YouTube 的唯一字符串因此不是基于完整的 16 字节 UUID,它们的 11 个字符 id 可能存储在数据库中以便于查找并基于较小的值。

【讨论】:

【参考方案2】:

对于那些专门寻找一种以 url 安全的方式缩短 uuid 的方法的人,@MartijnPieters 的真正有用的答案可以使用base64 模块来处理类似于评论的非 url 安全的字符。关于@okoboko 的答案(没有一些不必要的位)。

import base64
import uuid

# uuid to b64 string and back
uuid_to_b64str = base64.urlsafe_b64encode(uuid.uuid1().bytes).decode('utf8').rstrip('=\n')
b64str_to_uuid = uuid.UUID(bytes=base64.urlsafe_b64decode(f'uuid_to_b64str=='))

# uuid string to b64 string and back
uuidstr_to_b64str = base64.urlsafe_b64encode(uuid.UUID(str(uuid.uuid1())).bytes).decode('utf8').rstrip('=\n')
b64str_to_uuidstr = str(uuid.UUID(bytes=base64.urlsafe_b64decode(f'uuidstr_to_b64str==')))

【讨论】:

小心uuid1() - 如果在同一系统上同时调用多次(例如在循环中),它将返回相同的 UUID。所以你可能想考虑使用随机生成的uuid4() 函数(或不使用,取决于用例)。【参考方案3】:

您可以查看 Python 的 base64 模型。 GUID 本质上是数字的 base-16 表示,您可以删除连字符,从 base 16 解码,然后编码为 base 64。反过来需要从 base 64 解码,在 base 16 中编码,然后插入连字符在适当的地方。

【讨论】:

以上是关于将 UUID 32 个字符的十六进制字符串转换为“YouTube 风格”的短 id 并返回的主要内容,如果未能解决你的问题,请参考以下文章

JAVA生成UUID

UUID 不同版本的区别及选择

生成一个唯一的ID

Java技术 | 细谈Java中UUID的简单了解与使用

C#如何将大的十六进制字符串转换为二进制

使用 0x (C#) 将十六进制字符串转换为 int32 [关闭]