如何将 Base64 解码为 UTF-8
- 把 Base64 粘贴到 输入 · Base64。字符集 已预设为 UTF-8。
- 在 输出 · 文本 中查看结果。状态行会显示字节数和检测到的内容类型。
- 如果状态行提示“部分字节不是有效的 UTF-8”,说明文本使用的是其他字符集。请试试 自动检测、Windows-1252 或 UTF-16LE。
- 复制输出 复制文本;下载 原样保存解码后的字节。
反方向的编码请使用 UTF-8 转 Base64。
示例
| Base64 | 按 UTF-8 解码 | 说明 |
|---|---|---|
8J+YgA== | 😀 | 一个 Emoji,4 个字节 |
4oKs | € | 3 个字节,不需要填充 |
bmHDr3ZlIGNhZsOp | naïve café | 重音字母,每个 2 个字节 |
77u/SGk= | Hi | 5 个字节:字节序标记加上 Hi |
Y2Fm6Q== | caf� | E9 是 Latin-1 的 é,在 UTF-8 中无效 |
8J+Y | � | 一个 Emoji 在 4 个字节中的第 3 个之后被截断 |
Y2Fmw4PCqQ== | café | 本身已是乱码的文本,其 UTF-8 编码有效 |
最后三行就是下文讨论的问题:字符集不对、字符串被截断,以及在编码前就已成为乱码的文本。
无效的 UTF-8 与替换字符
UTF-8 的规则很严格:大于 7F 的字节必须属于一个 2、3 或 4 字节的序列,并符合固定的模式。不符合模式的字节无法按 UTF-8 读取。解码器会把每个损坏的序列替换为 U+FFFD(�),并给出提示“部分字节不是有效的 UTF-8,可能出现乱码,请尝试其他字符集”。不会悄悄丢掉任何内容,因此每个 � 都准确标出了问题所在的位置。
几乎所有情况都出自以下两个原因:
- 字符集不同。
Y2Fm6Q==包含63 61 66 E9,在 ISO-8859-1 和 Windows-1252 中是café。切换 字符集,文本就会出现。Base64 解码器 页面按字符集列出了常见症状。 - 字符串被截断。 如果 Base64 被截断了,例如受数据库列长度或日志行长度的限制,最后一个字符可能会丢掉部分字节。
8J+YgA==是一个完整的 Emoji;8J+Y是同一个 Emoji 少了最后一个字节,解码后只剩一个�。请到来源处找完整的值。
双重编码的文本(é 和 ’)
如果输出中 é 显示为 é、ü 显示为 ü,或者弯引号显示为 ’,问题不在解码器。在此之前的某个环节,UTF-8 字节被当作 Windows-1252 读取,又重新存成了 UTF-8,于是每个特殊字符都变成了两三个错误的字符。Base64 如实保存了这段已损坏的文本:Y2Fmw4PCqQ== 是 7 字节字符串 café 的有效 UTF-8 编码。
在代码中,往往可以把这个错误倒过来修复这类字符串:先把文本按 Windows-1252 编码,再把得到的字节按 UTF-8 解码。真正的修复应该在写入数据的那个系统里完成。
字节序标记
有些编辑器(尤其是在 Windows 上)会在 UTF-8 文件开头写入字节 EF BB BF,即字节序标记。这样的文件编码成 Base64 后以 77u/ 开头。解码器能识别该标记并把它排除在文本之外,而 下载 会原样保留这些字节。在代码中,你可能需要自己去掉它:
import base64
raw = base64.b64decode("77u/SGk=")
raw.decode("utf-8") # '\ufeffHi'
raw.decode("utf-8-sig") # 'Hi'
base64.b64decode("Y2Fm6Q==").decode("utf-8", errors="replace") # 'caf�'
如果不加 errors="replace",Python 遇到无效字节会抛出 UnicodeDecodeError。在 JavaScript 中,new TextDecoder("utf-8", { fatal: true }) 会抛出 TypeError,而不是插入 �;默认的解码器会像本工具一样去掉 BOM。详见 Python 中的 Base64 和 JavaScript 中的 Base64。
常见问题
怎么把 Base64 解码成 UTF-8 字符串?
先把 Base64 解码成字节,再把这些字节按 UTF-8 解释。上面的工具一步完成。在 Python 中是 base64.b64decode(s).decode("utf-8");在浏览器中是 new TextDecoder().decode(bytes)。
输出里的 � 是什么意思?
它是 U+FFFD,即替换字符。只要字节构不成有效的 UTF-8,解码器就会在那里插入它,通常说明文本是用其他字符集编码的,或者 Base64 被截断了。
为什么解码后的文本开头有一个看不见的字符?
源数据很可能带有 UTF-8 字节序标记(Base64 以 77u/ 开头)。本工具会去掉它,但 Python 的普通 .decode("utf-8") 会把它保留为 \ufeff;改用 "utf-8-sig" 即可去掉。
我的 Base64 应该选 UTF-8 吗?
多半是的。JSON、Web、Linux 和现代 API 都使用 UTF-8。来自旧版 Windows 程序或 .NET、PowerShell 字符串的数据,则可能是 Windows-125x 或 UTF-16。