本文面向希望理解 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 位二进制数的取值范围是
000000~111111,共 64 种组合,正好对应字符表中的 64 个字符。2. 起源与应用
Base64 最初用于解决文本协议不能直接安全承载任意二进制字节的问题。早期电子邮件系统主要处理 ASCII 文本,因此需要先把附件等二进制数据转换为文本形式。此后,Base64 也被用于 HTTP、JSON、XML 等场景。
2.1 常见应用
- 在电子邮件中传输附件;
- 使用 Data URI 在网页中嵌入小型资源;
- 在 HTTP Basic 认证中表示用户名和密码;
- 在 JSON、XML 等文本格式中承载二进制数据。
HTTP Basic 认证中的 Base64 结果可以直接还原,因此不能把它当作密码保护机制。
2.2 编码示例
以字符串
abc 为例,其 ASCII 编码分别为 0x61、0x62 和 0x63:编码过程如下:
- 将 3 个字节连接为 24 位数据:
01100001 01100010 01100011;
- 拆成 4 组 6 位:
011000 010110 001001 100011;
- 转换为十进制索引:
24、22、9、35;
- 查 Base64 字符表,得到
Y、W、J、j。
因此,
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 字节时使用
= 补齐输出长度。它解决的是二进制数据的文本化传输问题,不提供加密、完整性校验或压缩能力。实现解码器时,除了还原位组,还要检查输入长度、字符表、填充位置和尾部未使用位,避免接受格式不合法的数据。