← 中文资源

简体中文版本

Crockford Base32 整数编解码

用 Crockford Base32 编码整数,并解析允许连字符、字母 O 与 I 等易混字符的人工输入。

阅读英文原文

功能说明

Crockford Base32 是一种为人工读写设计的编码:字母表去掉容易与数字混淆的 I、L、O、U,解码时又把 I、L 视为一,把 O 视为零,并允许用连字符分组。它常用于短链接、订单号、设备标识和需要电话口述的编号。相比十六进制,它更短;相比 Base64,它不含大小写敏感字符和加号斜杠,复制到表格或 URL 中不易损坏。

本例实现无符号整数的编码与解码。编码从低位开始不断除以三十二,把余数映射到字母表后反转。解码逐字符累加,把前值乘三十二再加上当前字符值。

关键代码

ALPHABET 共三十二个字符,顺序为数字零到九、去掉 I L O U 的大写字母。DECODE 先按字母表建立映射,再补充 I、L 到一和 O 到零的别名。encode 拒绝负数,零直接返回字符零,其余情况循环取余并反转结果。decode 先统一大写并去掉连字符,空输入报错,遇到映射表之外的字符立即抛出异常,防止静默跳过导致错号。main 对同一个数编码再解码,并比较带连字符与不带连字符的解析结果。

使用注意

编码结果不包含校验位,人工输入错误可能被解析成另一个合法编号;需要防错时应追加模三十七校验字符并在解码后验证。编码长度不固定,不能按字符串长度推断数值范围。解码不检查前导零,多个字符串可能对应同一个数。该方案只适用于非负整数,负数、浮点和任意字节串需要另行设计。若编号需要按时间排序,应结合时间戳或使用专门的有序标识方案。

验证方式

运行脚本,一亿二千三百四十五万六千七百八十九编码为 3NQK8N,解码后回到原值。带连字符的 3N-QK8N 与不带连字符的 3NQK8N 输出相同,说明人工分组被正确忽略。把输入中的数字零改成字母 O,解码结果应保持不变,验证易混字符别名生效。

代码示例

ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
DECODE = {char: index for index, char in enumerate(ALPHABET)}
DECODE.update({"I": 1, "L": 1, "O": 0})


def encode(number: int) -> str:
    if number < 0:
        raise ValueError("number must be non-negative")
    if number == 0:
        return "0"
    digits: list[str] = []
    while number:
        number, remainder = divmod(number, 32)
        digits.append(ALPHABET[remainder])
    return "".join(reversed(digits))


def decode(text: str) -> int:
    cleaned = text.upper().replace("-", "")
    if not cleaned:
        raise ValueError("input is empty")
    number = 0
    for char in cleaned:
        if char not in DECODE:
            raise ValueError(f"invalid character: {char!r}")
        number = number * 32 + DECODE[char]
    return number


if __name__ == "__main__":
    value = 123456789
    token = encode(value)
    print(token, decode(token))
    print(decode("3N-QK8N"), decode("3NQK8N"))