本文面向希望理解 Base64 工作方式并使用 C++ 实现编解码的读者。文章从 6 位分组、字符表和填充规则开始,最后给出包含输入校验的 C++17 实现。

1. Base64 编码的基本原理

Base64 是一种用 64 个可打印字符表示二进制数据的编码方式。它把连续 3 个字节,也就是 24 位二进制数据,拆成 4 组 6 位数据,再将每组映射为一个 Base64 字符。
Base64 只改变数据的表示形式,不提供加密或压缩能力。

1.1 Base64 字符表

标准 Base64 使用以下 64 个数据字符:
填充符 = 不属于这 64 个数据字符,只用于补齐编码结果的长度。

1.2 为什么按 24 位分组

一个字节包含 8 位,而一个 Base64 字符承载 6 位。24 是 8 和 6 的最小公倍数,因此 3 个字节可以刚好拆成 4 组 6 位数据:
6 位二进制数的取值范围是 000000111111,共 64 种组合,正好对应字符表中的 64 个字符。

2. 起源与应用

Base64 最初用于解决文本协议不能直接安全承载任意二进制字节的问题。早期电子邮件系统主要处理 ASCII 文本,因此需要先把附件等二进制数据转换为文本形式。此后,Base64 也被用于 HTTP、JSON、XML 等场景。

2.1 常见应用

  • 在电子邮件中传输附件;
  • 使用 Data URI 在网页中嵌入小型资源;
  • 在 HTTP Basic 认证中表示用户名和密码;
  • 在 JSON、XML 等文本格式中承载二进制数据。
HTTP Basic 认证中的 Base64 结果可以直接还原,因此不能把它当作密码保护机制。

2.2 编码示例

以字符串 abc 为例,其 ASCII 编码分别为 0x610x620x63
编码过程如下:
  1. 将 3 个字节连接为 24 位数据:01100001 01100010 01100011
  1. 拆成 4 组 6 位:011000 010110 001001 100011
  1. 转换为十进制索引:2422935
  1. 查 Base64 字符表,得到 YWJj
因此,abc 的 Base64 编码结果是 YWJj

2.3 填充规则

当原始数据长度不是 3 的倍数时,最后一组不足 24 位,需要用 = 补齐编码结果:
  • 原始数据长度除以 3 余 0:不需要填充;
  • 原始数据长度除以 3 余 2:末尾添加 1 个 =
  • 原始数据长度除以 3 余 1:末尾添加 2 个 =
解码时,每 4 个 Base64 字符最多还原为 3 个字节;末尾的 = 表示最后一组缺少的原始字节。

3. C++ 编解码实现

下面的示例使用标准 Base64 字符表完成字符串编码和解码:
运行结果:
这个实现要求输入长度是 4 的倍数,只接受标准 Base64 字符,并检查填充位置和未使用的填充位。若输入不符合规则,解码函数会抛出 std::invalid_argument

4. 常见误区

  • Base64 是编码,不是加密;
  • Base64 通常会增大数据体积,不是压缩算法;
  • = 是填充符,不属于 64 个数据字符;
  • 标准 Base64 与 Base64URL 使用不同的第 62、63 号字符;
  • 是否允许换行和其他非字符表内容取决于上层协议,解码器不应无条件忽略异常输入;
  • 文本化后的内容仍需进行长度限制、格式校验和安全检查。

5. 总结

Base64 的关键是把 24 位输入拆成四组 6 位索引,并在输入不足 3 字节时使用 = 补齐输出长度。它解决的是二进制数据的文本化传输问题,不提供加密、完整性校验或压缩能力。
实现解码器时,除了还原位组,还要检查输入长度、字符表、填充位置和尾部未使用位,避免接受格式不合法的数据。

6. 参考资料