Base62 编码/解码

用 Base62 转换数字或字节串:62 个字符由数字、大写字母和小写字母组成,在 URL 查询串或 HTML 文本中都不需要转义。这正是短链接和自增 ID 采用它的原因。 两种模式:十进制数字(ID 变成短码)和字节(文本或十六进制输入)。另见:Base64 编码/解码 | Base32 / Base58 编码转换器 | Base91 编解码。
所有处理均在浏览器本地完成 — 您的数据绝不会被上传到任何服务器。

从没听说过 Base62?什么是 Base62 — 用大白话解释(附实例、三种字母表和常见陷阱)。
字母表:
把结果填充到固定宽度: 0 = 不填充。填充重复所选字母表的零字符,因此同一个数字在字母优先的字母表中填充方式不同。
工作原理:
输入是:
工作原理:
输出为:
在输入框中按 Ctrl+Enter 即执行当前模式。
请选择编码或解码并点击其操作按钮。自测按钮会运行数据文件中记录的每个示例、0 到 64 的每个字节长度以及一组十进制数字,遍历全部三种字母表,并在此报告数量。
三种字母表,全文印出: Base62 背后没有标准化组织,字母表在不同实现之间不一定相同。以下是目前实际出现的三种排列,每一种都逐字元连同其数值一起印出,因此任何结果都可以追溯到产生它的确切字母表。阴影格是代表 0 的字元,三种排列里并不相同。
Base62 在三种排列中都区分大小写:大写字母与它的小写形式几乎总是代表不同的值,因此经过小写化的代码不是错误,而是另一个数。
三种排序中 Base62 全都是大小写敏感的:一个大写字母和它的小写形式几乎总是对应不同的值,所以一个被小写化过的短码是另一个数字,而不是报错。
同样的字节在各编码中的长度:
本页接受的输入规模及其原因:这里的每种模式都要经过一个大整数转换,而大整数转换的耗时与长度的平方成正比。这是格式本身的性质,不是本页的问题:任何真正实现 Base62 的工具都有同样形状的限制。下面的数字来自以页面自身例程运行的计时测量,而非运行中的标签页,因此刻意偏悲观:桌面浏览器通常快几倍,这个余量正是为慢速手机准备的。限制的设定保证即使是慢速情形也能完成,而不是把标签页卡死。
超过软限制时,第一次点击会解释开销并要求再点一次确认;超过硬限制时,页面直接拒绝,而不是让标签页卡住几秒且无法显示进度。字节模式编码时按字节计数、解码时按字符计数,并始终取较大者:2,048 字节印出来约 2,751 个字符,已超过字符限制,因此能完整走完本页往返的载荷约为 1,520 字节以内。对任意实际规模的纯二进制数据,Base64 编码/解码 和 Base91 编解码 是线性的,没有此类限制。
什么是 Base62?

Base62 是一种只用在任何地方粘贴都能存活的字符来书写数字的方式:十个数字、26 个大写字母和 26 个小写字母。没有标点、没有空格、没有填充字符,没有任何需要先转义才能放进 URL、文件名、Cookie 或数据库键的东西。10 加 26 加 26 等于 62,名字由此而来。

30 秒版本:数字 12345 写作 3D7,1234567890 写作 1LY7VK。反方向是同一个运算的逆过程,但必须使用产生该短码的确切字母表 — 换一个字母表,同样的六个字符会变成另一个数字,而且没有任何报错。

本页做两件相关的事,它们的区别比本页其他任何事情都重要:

  • 整数模式把输入当作数字。像 1234567890 这样的十进制 ID 变成 1LY7VK,输入的前导零不属于值本身,因此不会回来。
  • 字节模式把输入当作字节串。字节被当作一个大端大整数读入,再按 62 整除,是对另一个量做的同样算术。这里前导零字节的数量会保留下来,每个零字节对应一个零字符。
62 从哪里来,代价是什么

62 不是 2 的幂,仅此一条事实就决定了其他一切。人们熟悉的编码是把输入切成固定比特组:4 比特印成一个十六进制字符,6 比特印成一个 Base64 字符,5 比特印成一个 Base32 字符。Base62 做不到这一点,因为没有整数的比特数能匹配一个 62 值的字符 — 5 比特只能表示 32 个值,6 比特是 64。所以 Base62 走的是算术路线:把整个输入读成一个整数,再以 62 为底印出。

于是一个字符承载 log2(62) = 5.954 比特,非常接近 Base64 的 6;在十进制模式下,一个字符顶替 log62(10) = 1.792 个十进制位。在决定长度之前,有几个值得知道的后果:

编码长度可表示的值实际意义
4 个字符624 = 14,776,336足够一个不超过八位数的计数器使用,且塞得进一个电话号码的宽度。
6 个字符626 = 56,800,235,584ID 短码的常见形态。十位十进制 ID 六个字符即可容纳。
7 个字符627 = 3,521,614,606,208比 32 位计数器的范围高出两个数量级,正好是一个短链接别名的宽度。
11 个字符6211 = 52,036,560,683,837,093,888超过 264 = 18,446,744,073,709,551,616,完整的 64 位数十一字符即可容纳。

反过来看,1,000 个十进制位需要约 558 个字符,4,096 个十进制位需要 2,285 个。节省是实在的,但有上限:Base62 把十进制数缩短约 44%,而且永远比不过最密的打印编码,因为每字符 5.954 比特就是比 6.4 或 6.5 少。

整数模式:前导零的下场

整数只有一种规范的书写形式,它以字母表的零字符开头 — 在两种数字优先的排序中是 0,在字母优先的排序中是 A。第一个有效字符左侧的一切都是填充,而填充不属于值:00012345 和 12345 都变成 3D7。如果需要固定宽度 — 比如一个要在列中对齐的七字符短码 — 本页的把结果填充到固定宽度输入框会重复零字符直到达到宽度,所以宽度为 7 时 12345 在数字优先字母表中印作 00003D7,在字母优先字母表中印作 AAAADNH。填充只是外观:那些前导字符对值毫无贡献,解码任何一个字符串都还原出 12345,因为无论用哪个字符书写零,0 × 626 和 0 × 625 都是零。

因此解码带填充的字符串同样可行,页面会报告前导字符中有多少个是零字符,让「短码有七个字符」和「值只需要三个字符」之间的差别不再成谜。

字节模式:对字节串做同样的算术

字节模式把输入字节当作一个大端整数读入 — 无论多长,整个字节串被视为一个数 — 再把这个数以 62 为底印出。一个字节有 8 比特,而一个字符只承载其中 5.954 比特,所以字节模式不是压缩:它需要输入尺寸的约 134%,比 Base64 的 133% 略差。以上方尺寸表使用的 92 字节样本为例,92 字节印作 124 个 Base62 字符,Base64 同样印作 124 个(其中 123 个在它的 = 填充之前)。Base62 在密度上输给 Base64,在字符安全性上赢回来,这正是每个短链接和邀请码有意做出的取舍。

前导零字节会被保留,而这一点在两个方向上都容易出错。两个前导零字节后跟 0x01,在数字优先字母表中印作 001,在字母优先字母表中印作 AAB,因为前导字符书写零,且每个零字节对应一个。单个 0x00 的字节串印作单个字符 0,单个 0x01 印作 1。这是字节模式和整数模式运行同一算法却看起来不同的唯一一处。

这一切的代价是字节模式继承了算术的平方级耗时:1,024 字节估计约三分之一秒,2,048 字节约 1.3 秒,与上方限制表给出的数字相同。这就是本页字节模式出方向封顶 2,048 字节、回方向封顶 2,048 字符的原因 — 2,048 字节印出来约 2,751 个字符,所以约 1,520 字节是能完整通过本页往返的最大载荷 — 也是本页字节模式只作小值参考、不能替代 Base64 编码/解码 或 Base91 编解码 处理真实文件的原因。

三种字母表,以及为什么字母表就是全部

字母表的字符是一个固定序列,一个字符的值就是它在这个序列中的位置。挪动序列就改变了每个字符的含义。三种排序被广泛使用,下面是同一个数在每一种中的三种写法:

十进制0-9 然后 A-Z 然后 a-z0-9 然后 a-z 然后 A-ZA-Z 然后 a-z 然后 0-9
000A
61zZ9
621010BA
12,3453D73d7DNH
1,234,567,8901LY7VK1ly7vkBViHfU

把最后三列当成一个字母表来读,它是一个自洽的方案。把它们当成可互换的来读,它就散架了,因为解码对字母表是全盲的,而且盲得最彻底:输入的任何字符都在全部三个字母表里,所以错误的字母表不会产生任何报错,只会产生另一个数字。

一个短码按 0-9 A-Z a-z 读按 0-9 a-z A-Z 读按 A-Z a-z 0-9 读
3D712,34513,957211,665
1LY7VK1,234,567,8901,624,950,79248,723,527,772

大小写是同一个陷阱更尖锐的版本。Base62 在每种排序中都大小写敏感,所以把短码小写化会悄悄改写它:1LY7VK 在数字优先字母表中是 1,234,567,890,而 1ly7vk 在同一字母表中是 1,624,950,792 — 它恰好又是 1,234,567,890 在数字后小写字母的字母表中的值,因为那个排序正是数字优先排序交换两个字母大小写后的结果。零字符也不共享:两种数字优先排序把零写作 0,而字母优先排序把它写作 A,所以那个字母表根本不以数字开头。

Base62 与其他编码的对比
编码每字符比特数相对输入的体积备注
Base16(十六进制)4200%每字节两个字符,一眼可读,无需查字母表。
Base325160%大小写不敏感、适合人口述,是这组里最浪费的。
Base36(0-9 a-z)约 5.17约 155%只有数字和小写字母,因此能挺过小写化流水线。12345 写作 9ix。
Base58约 5.86约 137%为地址而非密度而生:去掉四个易混淆字符而不是给它们排序。见 Base32 / Base58 编码转换器。
Base62约 5.95约 134%每个字符都是数字或字母,永远无需转义。密度介于 Base58 与 Base64 之间。
Base646133%各处的默认选择,代价是 +、/ 和 = 这三个强制转义的字符。见 Base64 转换器。
Ascii85 / Base856.4125%4 字节 5 字符,但按 85 整除,其字母表需要加引号。见 Ascii85 转换器。
Base91约 6.5约 123%这组里密度最高的,也是输出未经转义就粘贴最不安全的。见 Base91 编解码。

百分比是四舍五入后的渐近开销;对一个真实 92 字节输入的实测字符数在本页上方的尺寸表里。整数模式下比较反过来看,因为输入是十进制文本而非字节:此时 Base62 比它替换的数字短约 44%,Base36 短约 36%,这就是 ID 短码通常用 Base62 或 Base36 而非 Base58 的原因。

Base62 不是什么
  • 不是标准。没有 RFC,也没有规范。从未有标准组织定义过 Base62,这正是字母表没有保证的原因,也是本页把三种排序全部印出而不是悄悄挑一个的原因。
  • 不是加密。只是换了一次基,仅此而已。任何知道字母表的人一遍就能还原,没有密钥、没有盐、没有任何秘密。
  • 不是压缩。字节模式让数据增长约三分之一。整数模式看似压缩,只是因为十进制文本本来就是写数字的低效方式。
  • 不是大小写不敏感。Base32 可以小写后再读回来;Base62 不行。一个经过大小写规范化的系统的短码就是损坏的短码。
  • 不能自校验。没有校验和也没有校验字符,单个错误字符会给出一个看起来有效但值不同的结果。如果这很重要,Bitcoin 排序自带校验和 — 见 Base32 / Base58 编码转换器 的 Base58Check 一节。
  • 不是定长。字符数随值增长,一个从 1 涨到 627 的计数器会从一个字符变成八个。需要稳定列宽时请填充到固定宽度。
真正咬人的陷阱
  • 十有八九的谜团,答案都是字母表。如果另一个工具对同一个数给出不同的短码,先逐字符比较两边的字母表,再怀疑是否有 bug。上表显示了同一个值在三个字母表中的样子,三者全都正确。
  • 错误的字母表静默失败。它没有报错路径:3D7 可以解码成 12,345、13,957 或 211,665,只取决于假定了哪种排序。检查首字符(它最可能是填充零),并检查字母表开头是数字还是字母。
  • 前导零有两种不同含义。整数模式中 00012345 和 12345 是同一个数,印出来一模一样。字节模式中前导零字节是数据,会印作一个额外的零字符。丢掉其中一个字符就改变了字节串。
  • 大小写规范化会毁掉短码。电子邮件地址、主机名或 Windows 路径都可能在传输中被小写化。Base62 短码挺不过这种处理,这正是 Base36 仍然存在的原因。
  • 长输入不只是大,还慢。转换是对整个输入一次性做的算术,输入长度翻倍,时间翻四倍。本页在超过实测限制时直接拒绝而不是挂起;上方尺寸表给出了实测数字。
  • Base62 字符串本身证明不了任何事。既然几乎任何数字字母串都能无抱怨地解码,任意 URL 别名都会解出某个数字。短码只有当发放它的系统同时保存了映射时才有意义。
快速问答

为什么另一个 Base62 工具对同一个数给出不同的短码?

因为字母表不同。Base62 没有可供分歧的规范,实现者各选一种排序:数字和大写在前、数字和小写在前,或字母在数字之前。切换本页顶部的字母表下拉框,就能并排看到三种答案。

Base62 在 URL 里安全吗?

安全,在全部三种排序中都安全。全部 62 个字符都是无需转义的字母和数字,查询串里没有任何一个会被展开成百分号三元组,也没有任何一个会终结 URL、路径段或 HTML 属性。这正是短链接用 Base62 而非 Base64 的全部原因。

Base62 和 Base64 哪个更小?

对字节而言,Base64 略小一点:约为输入的 133%,Base62 约 134%。对上面的 92 字节样本,两者持平,都是 124 个字符。对十进制数字而言,Base62 把文本缩短约 44%,这才是实际的收益所在。

能用本页解码 Base64 字符串吗?

不能,而且它不会告诉你失败了。两个字母表在全部 62 个字符上重叠,所以 Base64 字符串通常在本页也是「合法」输入,只会解出另一个数字。解码 Base64 请用 Base64。

Base62 短码有最大长度吗?

理论上没有,算法没有上限。实际上本页止步于 4,096 个十进制位或 2,048 字节,因为它背后的算术是平方级的,标签页会卡死而屏幕上没有任何解释。本页上方的限制表列出了实测数字。

怎么知道本页的结果是对的?

按自测。它会运行每个已记录的示例、0 到 64 的每个字节长度和一组十进制数字,遍历全部三种字母表,把返回值与输入比较,并复查每个字母表的 62 个字符在 URL 中是否全部安全。示例按钮还会把它的三个结果与本页数据文件中记录的值对比,那些值已由本站测试工具中第二套独立的大整数实现验证过。

标准与致谢

这里没有可供实现的标准:Base62 是一种约定,不是规范,上面印出的三种字母表就是真实软件中出现的那三种。本页用本站自己的大整数例程实现它,直接在数字数组上做普通算术,因此任何尺寸都不会被 JavaScript 的 number 类型限制。

本页文字为本站撰写。页面上每个算过的例子都由页面自己的代码产生,并经本站测试工具中第二套基于原生大整数的独立实现验证;同一实现也验证了数据文件中的示例表和尺寸表。