编码格式全景:Base64 / Base32 / Hex 对比与选型决策

三种编码格式的本质

二进制数据无法直接以文本形式传输或显示,需要通过**编码(Encoding)**将字节序列映射为可打印 ASCII 字符。Base64、Base32 与 Hex 是三种最主流的二进制→文本编码方案,它们的本质区别只有一个:每个字符承载多少位

编码每字符位数字符集大小字符集范围膨胀率
Hex4 位160-9A-F100%
Base325 位32A-Z2-7(RFC 4648)或 0-9A-Z 去 I/L/O/U(Crockford)60%
Base646 位64A-Za-z0-9+/33%

每字符承载的位数越多,编码后体积越小,但字符集更大、可读性更差。这是一条基础权衡曲线:体积 ↔ 可读性

Hex:最直观的编码

工作原理

Hex(十六进制)将每个字节(8 位)拆分为两个 4 位段,分别映射到 0-9A-F 共 16 个字符。例如字节 0x48(即字符 H 的 ASCII)编码为字符串 "48"

字节:  H        e        l        l        o
Hex:  48       65       6c       6c       6f

5 种输出格式

Hex 编码虽简单,但实际使用中有 5 种主流输出格式:

  1. 连续48656c6c6f — 最紧凑,适合传输
  2. 空格分隔48 65 6c 6c 6f — 易读,常用于调试输出
  3. 0x 前缀0x48 0x65 0x6c 0x6c 0x6f — C/C++/Rust 字面量风格
  4. C 数组{ 0x48, 0x65, 0x6c, 0x6c, 0x6f } — 嵌入式固件字节数组
  5. Hex dump(xxd 风格):每行 16 字节,含偏移量、hex 部分、ASCII 部分
00000000: 48 65 6c 6c 6f                                    Hello

应用场景

  • 二进制调试:Wireshark、hexdump、xxd 的输出格式
  • 颜色值#FF5733 即 RGB 三通道的 Hex 表示
  • MAC 地址00:1A:2B:3C:4D:5E
  • Magic Number:文件头标识(如 PNG 文件以 89504e47 开头)
  • 哈希摘要:SHA-256 默认输出 64 字符 Hex 字符串
  • 嵌入式开发:C 数组导出固件字节数据
  • 加密密钥:AES-128 密钥以 32 字符 Hex 表示

优缺点

  • ✅ 最直观,每个字节固定 2 字符,便于按字节定位
  • ✅ 字符集最小(16 字符),任何场景都不会转义
  • ✅ 大小写不敏感(解码时统一处理)
  • ❌ 体积膨胀 100%,是三种编码中最大的
  • ❌ 不适合大体积数据传输

Base32:人类友好的编码

RFC 4648 标准

RFC 4648 标准的 Base32 字符集为 A-Z(26 字母)+ 2-7(6 数字)共 32 字符。选择 2-7 而非 0-9 是为了避开 0(与 O 易混)和 1(与 I/L 易混)。

每 5 字节(40 位)数据拆分为 8 个 5 位段,每段映射到一个字符。不足 5 字节的尾部用 = 填充至 8 字符的倍数。

字节:  H        e        l        l        o
二进制:01001000 01100101 01101100 01101100 01101111
5位段: 01001 00001 10010 10110 11000 11011 00011 01111
字符:  J      B      S      W      Y      3      D      P

「Hello」编码为 JBSWY3DP

Crockford 变体

Douglas Crockford 设计的 Base32 变体专为人工输入场景优化:

  • 字符集 0-9A-Z 去除 I/L/O/U(22 字母 + 10 数字 = 32)
  • 解码时自动归一化易混字符:I/L1O0
  • 不使用 = 填充
  • 支持校验和:将原始字节序列视为大整数 mod 37,附加 1 个校验字符

应用场景

  • TOTP 共享密钥:Google Authenticator 的 otpauth URI 中密钥用 Base32 编码
  • 账号号码:银行账号、订单号,配合校验和识别输入错误
  • 密钥指纹:SSH/PGP 公钥指纹,便于人工核对
  • DNS 编码:DNS 协议不区分大小写,Base32 是天然适配
  • 二维码短链:纯字母数字的二维码识别率更高

优缺点

  • ✅ 不区分大小写,适合口述、手写
  • ✅ 字符集仅字母数字,URL 友好,二维码友好
  • ✅ Crockford 校验和可识别单字符输入错误
  • ❌ 体积膨胀 60%,比 Base64 大
  • ❌ RFC 4648 的 = 填充在某些场景需额外处理

Base64:体积最小的编码

工作原理

Base64 字符集为 A-Za-z0-9+/ 共 64 字符,每 6 位映射到一个字符。每 3 字节(24 位)数据编码为 4 个字符。

字节:  H        e        l
二进制:01001000 01100101 01101100
6位段: 010010 000110 010101 101100
字符:  S      G      V      s

「Hel」编码为 SGVs

变体

  • 标准 Base64:字符集含 +//,使用 = 填充
  • URL 安全 Base64+-/_,可选省略填充
  • Base64URL:JWT、JWE 等场景的默认编码

应用场景

  • 图片内联:Data URL data:image/png;base64,...
  • JWT/JWE:三段式 JWT 与五段式 JWE 均用 Base64URL
  • 邮件附件:MIME Base64 编码二进制附件
  • CSS 资源:小图标内联减少 HTTP 请求
  • API 传输:二进制数据通过 JSON 文本传输

优缺点

  • ✅ 体积膨胀仅 33%,是三种编码中最小
  • ✅ 通用性最强,几乎所有编程语言内置
  • ❌ 含 +///= 特殊字符,URL 不友好(需 URL 安全变体)
  • ❌ 大小写敏感,不适合口述
  • ❌ 字符集含易混字符(0/O1/l/I

三种编码的对比

膨胀率对比

原始字节数Hex 长度Base32 长度Base64 长度
128(含 6 填充)4(含 2 填充)
368(含 1 填充)4
51088(含 2 填充)
244840(含 2 填充)32
100200160136(含 1 填充)

可读性对比

维度HexBase32(Crockford)Base64
大小写敏感
字符集大小163264
含特殊字符是(+/=)
易混字符处理I/L→1, O→0
校验和Crockford mod 37
二维码友好
口述友好高(0-9A-F)高(去除易混)低(大小写 + 符号)

编码后体积对比

原始数据(5 字节):Hello
Hex:        48656c6c6f              (10 字符, 膨胀 100%)
Base32:     JBSWY3DP                (8 字符, 膨胀 60%)
Base64:     SGVsbG8=                (8 字符, 膨胀 60%, 含 1 填充)

注意:5 字节是 Base64 的「最差情况」(3 字节的倍数 + 2 字节尾部),实际大体积数据下 Base64 膨胀率稳定在 33%。

选型决策树

数据是否需要人工输入或口述?
├─ 是 → 数据是否需要校验和识别输入错误?
│       ├─ 是 → Crockford Base32(含校验和)
│       └─ 否 → 是否为 TOTP 共享密钥或 DNS 场景?
│               ├─ 是 → RFC 4648 Base32
│               └─ 否 → Crockford Base32(无校验和)
└─ 否 → 是否为 URL/二维码/OCR 场景?
        ├─ 是 → Base32(纯字母数字,二维码识别率高)
        └─ 否 → 是否需要按字节定位或调试?
                ├─ 是 → Hex(每字节固定 2 字符)
                └─ 否 → 体积是否敏感?
                        ├─ 是 → Base64(膨胀最小 33%)
                        └─ 否 → Base64(通用性最强)

简化版选型口诀

  • 要人工输入 → Base32(Crockford 变体 + 校验和)
  • 要最小体积 → Base64(URL 安全变体用于 URL 场景)
  • 要字节级调试 → Hex(xxd 风格 Hex dump)
  • 要二维码识别率 → Base32(纯字母数字)
  • 要 TOTP 密钥 → Base32(RFC 4648,RFC 6238 标准要求)
  • 要 JWT/JWE → Base64URL(RFC 7515/7516 标准要求)
  • 要颜色值/MAC 地址 → Hex(行业惯例)

JavaScript 实现要点

Hex 编码

// 最简实现:每字节转 2 字符
function encodeHex(text) {
  const bytes = new TextEncoder().encode(text);
  return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
}
// "Hello" → "48656c6c6f"

Base32 编码(RFC 4648)

function encodeBase32(bytes, alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') {
  let output = '';
  let buffer = 0;  // 累积位
  let bits = 0;    // 当前累积位数
  for (const byte of bytes) {
    buffer = (buffer << 8) | byte;
    bits += 8;
    while (bits >= 5) {
      bits -= 5;
      output += alphabet[(buffer >> bits) & 0x1f];
    }
  }
  // 处理剩余不足 5 位的尾部
  if (bits > 0) {
    output += alphabet[(buffer << (5 - bits)) & 0x1f];
  }
  // RFC 4648 填充 = 至 8 的倍数
  while (output.length % 8 !== 0) output += '=';
  return output;
}

Base64 编码

// 浏览器原生 btoa 仅支持 Latin1,需手动转 UTF-8 字节
function encodeBase64(text) {
  const bytes = new TextEncoder().encode(text);
  let binary = '';
  for (const b of bytes) binary += String.fromCharCode(b);
  return btoa(binary);
}
// "Hello" → "SGVsbG8="

关键陷阱

  1. UTF-8 编码:直接用 btoa(text) 处理中文会抛 InvalidCharacterError,必须先 TextEncoder().encode()
  2. Crockford 校验和精度value mod 37 直接计算会因 Number.MAX_SAFE_INTEGER 丢失精度,需逐字节累积取模:mod = (mod * 256 + byte) % 37
  3. Hex dump 解析:xxd 风格输出含偏移量、双空格分隔符、ASCII 部分,不能简单移除所有非 hex 字符,需先正则识别格式再提取
  4. Base64 URL 安全:JWT/JWE 场景需将 +-/_= 省略

真实应用场景对比

场景 1:存储用户密码重置令牌

令牌(Hex):        a3f5b2c8d1e4f6a7b8c9d0e1f2a3b4c5
令牌(Base32):     7IY7YULGOJXV3LQBE5QVYLZ5XJQV3XQF
令牌(Base64URL):  o_WyyNHk9qe4ydDh8qO0xQ
  • 选 Hex:便于数据库存储与按字节调试
  • 选 Base32:便于用户复制粘贴(不区分大小写)
  • 选 Base64URL:体积最小,适合 URL 参数

场景 2:TOTP 共享密钥

密钥(原始字节):  48 65 6c 6c 6f
密钥(Base32):    JBSWY3DP
密钥(Hex):       48656c6c6f

RFC 6238 TOTP 标准要求密钥以 Base32 编码,原因是:

  1. 不区分大小写,用户手动输入容错
  2. 字符集仅字母数字,二维码识别率高
  3. +///= 等需转义的字符

场景 3:嵌入式固件字节数组导出

// C 数组格式(Hex 变体)
const uint8_t firmware[] = {
    0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f,
    0x72, 0x6c, 0x64, 0x21, 0x0a
};

这是 Hex 编码的 C 数组变体,嵌入式开发中固件烧录、Bootloader 升级、字库数据导出等场景的标准格式。

场景 4:哈希摘要展示

SHA-256("Hello")(Hex):   185f8db32271fe25f561a6fc938b2e26...
SHA-256("Hello")(Base64):GF+NuyMi3/4l9WGm/JK0FE1tsYU=

行业惯例是 Hex 展示,原因:

  1. 固定 64 字符,便于按字节比对
  2. 大小写不敏感,复制粘贴容错
  3. 不含特殊字符,任何终端都能显示

工具矩阵联动

本站的编码三工具形成「Base64 + Base32 + Hex」编码矩阵闭环:

用户可:

  1. Hash 工具计算摘要后,用 Hex 工具切换为 C 数组格式导出
  2. Base64 工具解码 JWT 后,用 Base32 工具重新编码用于 DNS 场景
  3. Base64 图片工具生成 Data URL 后,用 Hex 工具查看图片字节流
  4. TOTP 密钥(Base32)用 Hex 工具转字节序列后调试 OTP 算法

总结

Base64、Base32、Hex 不是相互替代的关系,而是各自适配不同场景的编码工具:

  • Base64 是「体积优先」的通用编码,适合机器间传输
  • Base32 是「人类优先」的友好编码,适合人工输入与二维码场景
  • Hex 是「调试优先」的直观编码,适合字节级查看与嵌入式开发

理解三种编码的字符集、膨胀率、可读性差异,才能在不同场景做出正确选型。掌握选型决策树的核心问题——「数据是否需要人工输入?」「是否需要按字节定位?」「体积是否敏感?」——就能快速定位到合适的编码方案。