Python UUID以特殊字符表示

1 投票
3 回答
2213 浏览
提问于 2025-04-15 19:22

在Python中创建一个UUID(通用唯一标识符),像这样:

>>> uuid.uuid1()
UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')

我想把这个UUID转换成一个字符串,这个字符串由大写字母A到Z组成,但不包括字母D、F、I、O、Q和U,还要加上数字0到9,以及字符“+”和“=”。也就是说,我想把它转换成由32个字符组成的字符串,这些字符比较容易被光学识别(OCR)识别:

[ABCEGHJKLMNPRSTVWXYZ1234567890+=]

我把这个字符集合称为OCRf集合(因为它对OCR友好)。

我想要一个同构的函数:

def uuid_to_ocr_friendly_chars(uid)
    """takes uid, an integer, and transposes it into a string made 
       of the the OCRf set
    """
    ...

我最初的想法是把UUID转换成32进制。例如:

OCRf = "ABCEGHJKLMNPRSTVWXYZ1234567890+="

def uuid_to_ocr_friendly_chars(uid):
     ocfstr = ''
     while uid > 1:
        ocfstr += OCRf[uid % 32]
        uid /= 32
     return ocfstr

不过,我想知道这种方法是否是转换的最佳和最快的方式,或者有没有更简单、更快的方法(比如内置函数、更聪明的算法,或者其他更好的方法)。

感谢你的建议。谢谢。

3 个回答

1
transtbl = string.maketrans(
  'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
  'ABCEGHJKLMNPRSTVWXYZ1234567890+='
)

uuidstr = uuid.uuid1()

print base64.b32encode(str(uuidstr).replace('-', '').decode('hex')).rstrip('=').translate(transtbl)

是的,这个方法让我有点不舒服,谢谢你问我。

1
>>> OCRf = 'ABCEGHJKLMNPRSTVWXYZ1234567890+='
>>> uuid = 'a8098c1a-f86e-11da-bd1a-00112444be1e'
>>> binstr = bin(int(uuid.replace("-",""),16))[2:].zfill(130)
>>> ocfstr = "".join(OCRf[int(binstr[i:i+5],2)] for i in range(0,130,5))
>>> ocfstr
'HLBJJB2+ETCKSP7JWACGYGMVW+'
>>> "%x"%(int("".join(bin(OCRf.index(i))[2:].zfill(5) for i in ocfstr),2))
'a8098c1af86e11dabd1a00112444be1e'

要再转换回来

2

你觉得把表示方式“压缩”18.75%重要吗?也就是从32个字符变成26个字符?因为如果节省这点字节不是特别关键的话,像 uid.hex.upper().replace('D','Z') 这样的做法就能满足你的需求(虽然没有用到你提供的整个字母表,但这样做的唯一代价就是错过了那18.75%的“压缩”)。

如果每一个字节都必须压缩到极致,那我建议你可以考虑把数据分成每段20位的子串——这相当于5个十六进制字符,或者说4个你自定义的字符。这样总共有6段(再加上剩下的8位,你可以像上面那样用 hex.upper().replace,因为做得更复杂也没什么好处)。你可以通过切片 .hex 来轻松获取这些子串,然后用 int(theslice, 16) 把每个子串转成整数。接着,你基本上可以用和之前一样的算法来处理,但因为这次处理的是更小的数字,所以速度会有明显提升。另外,构建字符串时不要用 += 循环——先把所有的“数字”放到一个列表里,最后用 ''.join 把它们连接起来,这样也能提高性能。

撰写回答