科普
|

Base91 编码详解:比 Base85 更紧凑的二进制文本编码方案

全面了解 Base91 编码的工作原理、91 个字符的字母表、基于位流的编解码算法、与 Base64/Base85 的效率对比,以及它在数据压缩传输等场景中的实际应用。

什么是 Base91?

Base91 是一种高效的二进制到文本编码方案,由 Joachim Henke 设计开发。它使用 91 个可打印 ASCII 字符来表示任意二进制数据,是目前在纯 ASCII 可打印字符范围内能实现的理论最紧凑编码方案之一。

与 Base64(膨胀率 33.3%)和 Base85(膨胀率 25%)相比,Base91 的平均膨胀率仅为 14%~23%,在绝大多数数据场景下编码输出最短。这意味着在带宽受限、存储敏感或需要将二进制数据嵌入文本的场景中,Base91 拥有无可比拟的效率优势。

在线工具推荐:需要进行 Base91 编码或解码?试试我们的在线 Base91 编解码工具,支持文本和十六进制输入,自动转换,一键复制结果。

Base91 字符表

Base91 使用 91 个可打印 ASCII 字符作为编码字母表。从 ASCII 33–126(0x21–0x7E)的 94 个可打印字符中,排除了连字号 -(ASCII 45)、单引号 '(ASCII 39)和反斜杠 \(ASCII 92)这三个在很多编程语言和传输协议中需要特殊转义或容易引起问题的字符,从而保留了尽可能多的可用字符:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~"

共 91 个字符,索引从 0 到 90:

索引范围字符
0–25AZ(大写字母)
26–51az(小写字母)
52–6109(数字)
62–90!#$%&()*+,./:;<=>?@[]^_{|}~”` (特殊符号)

设计理念:使用 91 个字符是因为 ASCII 33–126 共有 94 个可打印字符,去掉连字号 -、单引号 ' 和反斜杠 \ 后恰好剩余 91 个。而 91² = 8281 刚好大于 13 位能表示的最大值 8191,这使得两个 Base91 字符足以完整表示 13 位的数据空间。

Base91 的工作原理

Base91 与 Base64、Base85 有本质不同——它不使用固定的字节分组,而是基于位流(bitstream) 进行编码,以两个 Base91 字符为一组输出。

编码过程

  1. 构建位队列:将输入数据的每个字节按顺序推入一个位队列(bit queue),低位在前
  2. 取 13 位:当位队列中累积了超过 13 位时,取出低 13 位作为数值 v
  3. 条件判断
    • 如果 v > 88:使用这 13 位,即 v 不变
    • 如果 v ≤ 88:再多取 1 位(共 14 位),用 14 位值作为 v
  4. 输出两个字符:将 v 对 91 取余和整除,得到两个索引值,各映射为 Base91 字母表中的一个字符
  5. 处理尾部:当所有字节处理完毕后,如果位队列中还有剩余位,输出 1 或 2 个额外字符

数学基础

  • 13 位能表示的最大值为 2¹³ − 1 = 8191
  • 91² = 8281 > 8191,所以两个 Base91 字符可以表示 13 位数据
  • 14 位能表示的最大值为 2¹⁴ − 1 = 16383
  • 但由于仅当 13 位值 ≤ 88 时才会取 14 位,实际 14 位值的最大可能为 8192 + 88 = 8280,小于 91² = 8281,因此两个 Base91 字符也足以表示

编码示例

编码字符串 "Hello"

字节输入:  H(0x48)  e(0x65)  l(0x6C)  l(0x6C)  o(0x6F)

步骤 1:将 0x48 = 01001000 推入位队列
步骤 2:将 0x65 = 01100101 推入位队列
         位队列现在有 16 位
步骤 3:取低 13 位 → v = 0x6548 & 0x1FFF = 0x0548 = 1352
         1352 > 88 → 使用 13 位
         1352 % 91 = 78  → 字母表[78] = '>'
         1352 / 91 = 14 → 字母表[14] = 'O'
         输出 ">O",位队列剩余 3 位
...(后续字节继续类似处理)

解码过程

解码是编码的逆操作:

  1. 读取字符对:每次读取两个 Base91 字符,通过查表得到各自的索引值
  2. 还原数值v = 第一个索引 + 第二个索引 × 91
  3. 位数判定
    • 如果 v & 8191 > 88:向位队列写入 13 位
    • 否则:向位队列写入 14 位
  4. 输出字节:每当位队列中有 8 位或更多时,取出低 8 位作为一个解码字节
  5. 尾部处理:如果最后剩余一个字符,用其索引值直接推入位队列

13/14 位自适应的精妙之处

这一设计是 Base91 的核心创新:

  • 当 13 位值 > 88 时,13 位足以唯一确定两个 Base91 字符(因为 89 ~ 8191 的范围由 13 位完整覆盖)
  • 当 13 位值 ≤ 88 时,额外借用 1 位扩展到 14 位,进一步提高数据密度
  • 这种自适应策略使得平均每个字节的编码字符数接近理论极限

编码效率详解

理论分析

每对 Base91 字符可以编码 13 到 14 位数据,因此:

  • 最佳情况:每 14 位输入产生 2 个字符 → 膨胀率 = 2 / (14/8) − 1 ≈ 14.3%
  • 最差情况:每 13 位输入产生 2 个字符 → 膨胀率 = 2 / (13/8) − 1 ≈ 23.1%
  • 平均情况:对于随机数据,13 位值 ≤ 88 的概率仅为 89 / 8192 ≈ 1.1%,因此绝大多数情况下走 13 位路径,实际膨胀率非常接近 ~23%。只有在输入数据经过特定分布构造(如刻意让低 13 位频繁出现小值)时,才可能接近 14.3%。

与其他编码方案的效率对比

编码方案输入 : 输出膨胀率字符集大小
Base91位流自适应~14–23%91
Base854 : 525%85
Base643 : 433.3%64
Base62大整数运算~37%62
Base58大整数运算~37%58
Base325 : 860%32
Base16 (Hex)1 : 2100%16

数据量越大,Base91 的优势越明显。对于千字节级别以上的数据,相比 Base64 可以节省 10%~15% 的传输量。

实际编码长度示例

原始数据Base64 长度Base85 长度Base91 长度
10 字节16 字符13 字符12 字符
100 字节136 字符125 字符116 字符
1000 字节1336 字符1250 字符1154 字符
10000 字节13336 字符12500 字符11534 字符

Base91 的字符表设计

为什么排除这三个字符?

排除的字符ASCII 码排除原因
空格 32在很多协议和文件格式中作为分隔符,容易被截断或合并
反斜杠 \92在几乎所有编程语言的字符串中需要转义
单引号 '39在 SQL、shell 命令等场景中需要转义

与 Base85 字符表的区别

Base85(Ascii85)使用 ASCII 33–117 的连续字符,而 Base91 刻意跳过了空格、单引号和反斜杠,选择了一组在更多场景下可以直接使用的字符。这使得 Base91 编码结果在 JSON 字符串(仍需转义双引号 ")之外的大多数文本容器中可以直接嵌入。

常见应用场景

1. 高效数据嵌入

在需要将二进制数据嵌入纯文本环境(如电子邮件正文、日志记录、配置文件注释)时,Base91 提供了最紧凑的编码方案。每传输 1KB 的原始数据,Base91 比 Base64 节省约 100~180 字节。

2. 二维码与条形码

二维码(QR Code)的字母数字模式支持有限字符集。将二进制数据先用 Base91 编码可以在单个二维码中打包更多信息,比 Base64 增加约 8%~15% 的有效容量。

3. 网络协议与 API 传输

在自定义网络协议或 REST API 中需要以文本形式传输二进制载荷时,使用 Base91 可以减少消息体积,降低带宽消耗,尤其在移动网络等带宽受限环境中效果显著。

4. 嵌入式系统与 IoT

在存储和带宽极度受限的 IoT 设备和嵌入式系统中,Base91 的低膨胀率意味着固件更新包、传感器数据上报等场景可以用更少的字节完成传输。

5. 数据压缩辅助

虽然 Base91 本身不是压缩算法,但将压缩后的二进制数据进一步用 Base91 编码,可以在保持文本可传输性的同时最小化体积膨胀。这是 compress → Base91 encode → 文本传输 → Base91 decode → decompress 流水线中的理想选择。

6. 数据库文本字段存储

当需要在数据库的 VARCHAR/TEXT 字段中存储二进制数据(如小型图标、签名数据、序列化对象)时,Base91 可以比 Base64 节省约 8%~15% 的存储空间。

Base91 与其他编码方案的详细比较

特性Base91Base85Base64Base62Base58
字符集大小9185646258
膨胀率~14–23%25%33.3%~37%~37%
编码单位位流4→53→4大整数大整数
包含特殊符号是(+/=)
URL 安全❌ 需变体
固定编码比率❌ 可变✅ 4:5✅ 3:4❌ 可变❌ 可变
标准化Adobe 标准RFC 4648
实现复杂度中等较高较高较高
主要用途极致紧凑PS/PDF/Git通用传输短链接/ID加密货币

实现注意事项

  1. 位操作精度:Base91 的编解码依赖位级操作。在 JavaScript 中,位运算符作用于 32 位整数,因此位队列的值不能超过 32 位。实际实现中通常将位队列控制在安全范围内,每次取出 13 或 14 位后及时清除。

  2. 字符验证:解码时需验证每个输入字符是否在 91 字符的字母表中。非法字符的处理策略有两种:跳过(宽容模式)或报错(严格模式)。

  3. 尾部字符处理:编码结束时位队列中可能残留不足 13 位的数据。此时输出 1 个字符(如果剩余位数 ≤ 7 且值 ≤ 90)或 2 个字符(如果值 > 90 或剩余位数 > 7)。解码器需正确处理奇数长度的输入。

  4. 输出长度不固定:与 Base64 的 3:4 和 Base85 的 4:5 固定比率不同,Base91 的输出长度取决于数据内容。这使得预分配输出缓冲区需要按最差情况估算。

  5. 非 ASCII 兼容性:Base91 编码输出仅包含 ASCII 可打印字符,在 UTF-8 环境中完全兼容。但编码后的字符串包含双引号 " 等字符,在 JSON 中需要转义。

Base91 的优缺点

优点

  • 极致编码效率:在所有 ASCII 可打印字符编码方案中膨胀率最低
  • 广泛的字符选择:排除了最容易引起问题的空格、反斜杠和单引号
  • 流式处理友好:基于位流的设计天然支持流式编解码,无需预知数据总长度
  • 自包含:不需要 = 等填充字符,编码结果长度自然确定
  • 高效利用空间:对于大数据量,节省效果极其显著

缺点

  • 非标准化:没有 RFC 或其他正式标准定义,不同实现之间可能存在细微差异
  • 包含特殊字符:输出中包含 <>"& 等 HTML/XML 敏感字符
  • 非 URL 安全:包含 URL 保留字符,不适合直接用于 URL 参数
  • 可变长度输出:无法像 Base64 那样精确预计输出长度
  • 库支持有限:不如 Base64 那样在各种编程语言中有原生支持
  • 调试不便:编码结果不像 Base64 那样有广泛的在线工具支持

安全性考虑

  1. 编码不等于加密:Base91 只是数据表示方式的转换,不提供任何保密性。敏感数据必须先加密(如 AES、ChaCha20)再编码。

  2. 注入风险:Base91 编码结果包含 <>"& 等字符,直接嵌入 HTML、XML 或 SQL 语句中可能导致注入攻击。务必在相应的上下文中进行转义处理。

  3. 数据完整性:Base91 不包含内置校验和或纠错机制。如需保证传输数据的完整性,应在应用层添加 CRC32、SHA-256 等校验。

  4. 侧信道安全:对于安全敏感的应用(如密钥编码),需注意实现中是否存在基于时间或内存访问模式的侧信道泄漏。

如何选择合适的编码?

  • 需要最高编码效率,不介意特殊字符:选择 Base91
  • 需要较高效率且有行业标准支持:选择 Base85(Adobe PostScript/PDF)
  • 需要广泛兼容性和原生支持:选择 Base64(RFC 4648,几乎所有语言内置支持)
  • 需要 URL 安全和人类友好:选择 Base62Base58
  • 需要极简单的实现:选择 Base16 (Hex)

总结

Base91 是目前在 ASCII 可打印字符范围内效率最高的二进制文本编码方案。它通过精巧的 13/14 位自适应位流算法,将膨胀率压缩到 14%~23%,大幅优于 Base64 的 33.3% 和 Base85 的 25%。在数据嵌入、网络传输、IoT 设备等对体积敏感的场景中,Base91 是一个值得优先考虑的选择。

尽管 Base91 没有正式的标准化文件,也缺乏像 Base64 那样的原生语言支持,但其卓越的编码效率使它在特定场景中不可替代。赶快使用我们的 Base91 编解码工具 来亲身体验这种极致紧凑的编码方案吧!