如何进行 Base91 编码和解码
- 在 输入 · 文本 中输入或粘贴文本。编码后的字符串显示在 输出 · basE91 中。
- 对于二进制数据,把 从 设为 Hex 或 Base64,并以该格式粘贴。
- 要解码,在方向切换中点击 basE91 → 文本。输入中的换行会被忽略;遇到字母表以外的其他字符时,解码会停止并显示其位置。
- 用 复制输出 或 下载 获取结果。解码出的二进制数据请使用 保存字节。
basE91 的工作原理
basE91 由 Joachim Henke 发布。Base64 把每 6 个比特映射为一个字符。basE91 则把输入比特收集到一个队列中,每次取 13 个比特。两个字符可以表示 91 × 91 = 8281 个值,比 13 个比特所需的 8192 个略多。多出的空间用于一项优化:当接下来 13 个比特的值小于等于 88 时,编码器改取 14 个比特,仍然只写两个字符。
这使得输出长度取决于内容。对于随机数据,14 比特分组很少见(不到 1.1%),因此额外开销接近 16/13,约为 23%。零比特多的数据压缩得更好:1000 个零字节编码为 1143 个字符,额外开销为 14%。
| 输入 | basE91 | Base64 |
|---|---|---|
test | fPNKd | dGVzdA== |
Hello | >OwJh>A | SGVsbG8= |
Hello World! | >OwJh>Io0Tv!8PE | SGVsbG8gV29ybGQh |
| 4 个零字节 | AAAAA | AAAAAA== |
| 1000 个随机字节 | 约 1230 个字符 | 1336 个字符 |
可变分组有一个副作用:没有固定的块大小,因此不能从字符串中间开始解码,也不能在任意位置切分它。而 Base64 可以在任何 4 的倍数个字符处切分。
字母表及其带来的麻烦
basE91 的字母表是 A–Z、a–z、0–9 加上这 29 个字符:
!#$%&()*+,./:;<=>?@[]^_`{|}~"
它排除了空格、'、- 和 \。空格和 ' 在 shell 和带引号的字符串中会引起麻烦,\ 几乎在所有地方都是转义字符。但双引号仍在其中,还有 <、> 和 &,所以把 basE91 放进 JSON、HTML 或 XML 时需要转义,而转义会吃掉一部分相对 Base64 的优势。它最适合用在接受任意可打印 ASCII 的地方,例如文本文件、单引号包裹的 shell 参数或二进制安全的协议字段。
Python 实现的 basE91 编码器
标准库中没有 basE91。Henke 的算法很短,可以直接放进代码里:
ALPHABET = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~\"")
def b91encode(data: bytes) -> str:
out, queue, nbits = [], 0, 0
for byte in data:
queue |= byte << nbits
nbits += 8
if nbits > 13:
value = queue & 8191
if value > 88:
queue >>= 13
nbits -= 13
else:
value = queue & 16383
queue >>= 14
nbits -= 14
out += ALPHABET[value % 91], ALPHABET[value // 91]
if nbits:
out.append(ALPHABET[queue % 91])
if nbits > 7 or queue > 90:
out.append(ALPHABET[queue // 91])
return "".join(out)
b91encode(b"test") # 'fPNKd'
对于任何输入,它的输出都与本页一致。更多 Python 中处理字节的内容见 Python 页面。
什么时候选择 Base91
当输出的每个字节都很重要,而且通道几乎接受所有可打印 ASCII 时,选择 basE91:例如紧凑的日志,或有严格长度限制的文本字段。凡是要放进 JSON、URL、HTML 或电子邮件的内容,Base64 更安全,长度也差不多;如果你想要 25% 的额外开销和固定的块大小,Ascii85 是 PDF 和 PostScript 中的标准。
常见问题
Base91 使用哪些字符?
95 个可打印 ASCII 字符中,除空格、撇号 '、连字符 - 和反斜杠 \ 以外的全部字符。也就是字母、数字和 29 个标点符号,其中包括双引号。
Base91 比 Base85 更小吗?
略小一些。随机数据在 basE91 中增大约 23%,在 Base85 中正好增大 25%;在这里,100,000 个随机字节编码成了 122,976 个字符,而 Ascii85 是 125,000 个。含有大段零字节的数据还能更小,额外开销可低至约 14%。
为什么 Base91 用得不多?
它没有 RFC,输出中包含 " 等在 JSON、shell 和 HTML 中需要转义的字符,而且可变长度的比特分组让它比 Base64 更慢、更难实现。省下的那几个百分点很少能抵消这些缺点。
能解码其他库生成的 Base91 吗?
只有当它使用 Henke 的 basE91 字母表和比特顺序时才行,大多数库都是如此。少数库自带不同的 Base91 字母表;它们的输出在这里解码不会报错,但得到的字节是错的。