一、先学会认症状
| 你看到的 | 真实原因 | 能否还原 |
|---|---|---|
| 锟斤拷 | UTF-8 的替换字符 U+FFFD 被按 GBK 解码 | 基本不可逆,原始信息已丢 |
| 烫烫烫 / 屯屯屯 | 程序里未初始化的内存(0xCC/0xCD)被当文本打印 | 与编码无关,是代码 bug |
| ??? / 问号 | 目标编码不支持该字符,编码时被替换 | 不可逆 |
| é¤ 之类西欧字符 | UTF-8 字节被当成 Latin-1/Windows-1252 | 通常可逆 |
| 只有部分字乱 | 混合编码,或字体缺字(显示为方框) | 视情况 |
「锟斤拷」的来历值得记一下:UTF-8 遇到无法解码的字节会输出替换字符 U+FFFD,它的 UTF-8 字节是 EF BF BD;这段字节再被 GBK 解码,EF BF 正好对应「锟」,BD EF 对应「斤」,重复几次就成了「锟斤拷」。所以看到它基本可以判定:数据在某一环被错误解码过一次,已经损坏,不是简单换个编码就能修好。
二、乱码的唯一根因
写入和读取用了不同的编码。具体到工程里,常见的四类位置:
- 文件:编辑器按 UTF-8 保存,程序按 GBK 读;或反过来。
- HTTP:响应头
Content-Type: text/html; charset=iso-8859-1与实际内容不符;表单提交没声明编码。 - 数据库:连接串
characterEncoding与表字符集不一致;latin1表里存了 UTF-8 字节。 - 终端:
LANG=C的服务器上cat中文文件、或用 SSH 工具编码设置不对。
三、修复顺序(别跳步)
- 先确认原始字节。用十六进制看:
E4 B8 AD是 UTF-8 的「中」,D6 D0是 GBK 的「中」。看字节比看乱码字符可靠得多。 - 按源编码解码成正确文本。命令行可用
iconv -f GBK -t UTF-8 in.txt -o out.txt。 - 再按目标编码保存/输出,并让所有环节声明一致。
- 最后统一声明:HTML 加
<meta charset="UTF-8">,HTTP 头带 charset,数据库连接串写明编码。
跳过第 1 步直接「用工具转一下」,是把自己从一个乱码变成另一个乱码的最快方式。
四、几个易忽略的细节
- BOM:Windows 记事本保存的 UTF-8 带
EF BB BF头,会让 JSON 解析失败、PHP 输出多出空白、Excel 却能正确识别。需要给 Excel 看就加,需要给程序解析就去掉。 - 数据库的双重编码:
latin1列里存了 UTF-8 字节,读出来再转一次可能恰好「看着正常」,但长度、排序、索引全错。这种情况要修数据,不是改配置。 - URL 里的乱码:
%D6%D0是 GBK 的编码,现代系统应统一%E4%B8%AD(UTF-8)。 - 文件名乱码:压缩包在 Windows 打包、Mac 解压常乱,本质是 zip 文件名编码标记缺失,用
unzip -O GBK或专用工具处理。
五、怎么快速验证
拿不准某个字符的编码时,用 Unicode 转换工具 查它的码点与 UTF-8 字节,再用 URL 编码解码 对照百分号形式。两步对照,基本能定位是哪一环把字节读错了。
小结
锟斤拷 = 已被错误解码的 UTF-8(多半不可逆);烫烫烫 = 未初始化内存,不是编码问题;问号 = 目标编码不支持。修乱码的正确姿势永远是:先看字节,再定编码,最后统一声明。