Base64 编码原理:为什么中文解码会乱码

Base64 天天在用,但绝大多数「乱码」问题并不是编码算错了,而是字符集对不上。

一、先纠正一个最常见的误解:Base64 不是加密

Base64 只是把二进制字节重新排列成 64 个可打印字符,任何人拿到密文都能立刻还原。它解决的是「如何在只能传文本的通道里安全传二进制」,不是保密问题。把密码用 Base64 存储等同于明文存储。

二、编码过程:为什么 3 个字节变成 4 个字符

Base64 的名字来自它的字符表:A-Z、a-z、0-9、+、/,一共 64 个。每个字符能表达 6 个二进制位(26=64),而 1 个字节是 8 位,所以每 3 个字节(24 位)正好切成 4 组 6 位:

原始字节:  01001000 01101001 00100001     ("Hi!")
重新切分:  010010  000110  100100  100001
对应索引:    18      6       36      33
查表结果:    S       G       k       h
最终 Base64: SGkh

如果原文长度不是 3 的倍数,就用 = 补齐。所以你会看到结尾出现一个或两个等号——SGk=(2 字节)、SGkh(3 字节)。

补齐的 = 不是数据,只是占位符。很多解码器允许省略,但严格实现会拒绝长度不合法(非 4 的倍数)的输入——这就是「解码失败」的常见原因之一。

三、中文乱码的真正原因

关键点:Base64 编码的是字节,不是字符。同一个汉字在不同字符集里字节完全不同:

字符UTF-8 字节GBK 字节
中E4 B8 ADD6 D0
文E6 96 87CE C4

“中文”在 UTF-8 下是 6 个字节,Base64 结果是 5Lit5paH;在 GBK 下同样是 4 个字节,结果是 1tDOxA==。两边都能正确编解码,但如果用 UTF-8 去解 GBK 的字节,得到的自然是一串乱码或直接抛错。

所以排查乱码的顺序应该是:

  1. 先确认 Base64 解码本身成功(不报错);
  2. 再确认解出来的字节该用哪种字符集解释(UTF-8 / GBK / ISO-8859-1);
  3. 最后才怀疑编码环节。
浏览器里 atob() 返回的是「每字符一字节」的二进制字符串,直接当文本用会乱码。正确做法是把它转成 Uint8Array,再用 TextDecoder('utf-8') 解码。

四、URL 里的 Base64:为什么要换掉 + 和 /

标准 Base64 的 + 和 / 在 URL 中有特殊含义(+ 会被当成空格,/ 是路径分隔)。所以出现了 URL 安全变体(Base64URL,RFC 4648 §5):

标准URL 安全
+-
/_
保留 =通常省略 =

JWT 就用的这套变体。如果你拿标准解码器去解 JWT 的 payload,遇到 -、_ 会报「非法字符」——不是 token 坏了,是变体不同。

五、几个高频踩坑

六、动手验证

下面这些工具可以直接对照本文结论做实验,全部在浏览器本地运行,内容不会上传:

🔡Base64 编解码
文本与 Base64 互转,UTF-8 安全
🔟Hex 编解码
查看真实字节序列
🔣Unicode 转换
查看字符码点
🖼️图片转 Base64
生成 Data URL