中文乱码是怎么来的:看懂长相就能修

「锟斤拷」是替换字符连环套,「我」是 UTF-8 被 Latin-1 误读,「????」是写入时就丢了——乱码的长相对应固定的编码错位。一张长相速查表加三层排查法,多数乱码十分钟内可修。

约 7 分钟读完 · 更新于 2026-10-21 · 文中每条都能在下面的配套工具里动手验证

乱码的本质:字节没错,是「读的人」错了

文本从写入到展示要过好几道「编码 ↔ 解码」的关:编辑器、文件存储、网络传输、数据库、终端。只要「写入用的编码」和「读取时的假定编码」对不上,就产生乱码。好消息:错位的长相是有规律的——同一种错位永远产生同一种乱码长相,所以看长相就能反推病因。

长相速查表:看到什么,就是什么病

乱码长相病因修法
锟斤拷 / � 反复出现UTF-8 解码失败被替换成 U+FFFD 后再次转码,信息已丢从源头重取数据;本地无解
我是 之类拉丁扩展字符UTF-8 字节被按 Latin-1 / Windows-1252 解读按 Latin-1 重编回字节,再按 UTF-8 解
第一 , 等同款变体同上;页面缺 charset 声明时高发补 <meta charset="utf-8">;历史数据按上法修复
ÎÒÊÇ 之类大写拉丁堆GBK / GB2312 字节被按 Latin-1 误读按 Latin-1 还原字节,再按 GBK 解
清一色问号 ????写入时就丢了:目标编码没有这些字,被换成 ?改连接 / 列编码为 utf8mb4;已丢的数据无法恢复
方框口口(能复制出正常字)字体缺字形,数据其实是好的换字体;不用修数据

两种最常见错位的修复链(附代码)

UTF-8 被误读成 Latin-1(最常见)
const fixed = new TextDecoder("utf-8")
  .decode(new TextEncoder().encode(garbled));
// 原理:把「被误读的字符」按 latin1 编回原始字节,再按 utf-8 正确解码
// Python 等价:garbled.encode("latin-1").decode("utf-8")
GBK 被误读成 Latin-1(老系统对接)
// Node 无内置 GBK 解码器,用 iconv-lite:
const fixed = iconv.decode(Buffer.from(garbled, "latin1"), "gbk");
// Python 等价:garbled.encode("latin-1").decode("gbk")

排查三问:谁写的、谁读的、哪一层错位

  1. 用十六进制看原始字节(编辑器 hex 视图或 xxd):「你」的 UTF-8 是 E4 BD A0——字节对,就是读的层错了;字节本身是 GBK 序列(C4 E3),就是写的层错了。
  2. 按链路逐层确认编码声明:文件保存编码 → HTTP Content-Type charset → HTML meta → 数据库连接字符集(SET NAMES / connectionCharset)→ 列 / 表字符集。
  3. 只修最短的那一层:错位只发生在一处。把整条链路统一成 UTF-8 是长期解,当次修复先纠正错位的那一层。

MySQL 中文乱码:三处 utf8mb4 缺一不可

  1. 连接字符集:连接串加 characterEncoding=utf8mb4(JDBC)或连上后 SET NAMES utf8mb4——老驱动默认 latin1 是历史第一坑。
  2. 库表列:ALTER 到 utf8mb4(注意 MySQL 的 utf8 是残缺的三字节实现,存不了 emoji 与部分生僻字)。
  3. 写入侧:程序发出的字节本身必须是 UTF-8——上游文件是 GBK 时先转码再入库。
  4. 已入库的乱码数据:若只是展示错位(长度不变、无 ? 和 �),可按修复链抢救;若已含 ? 或 U+FFFD,信息已丢,只能重灌。

预防:把「编码」从默认行为变成显式声明

  • 全链路统一 UTF-8(无 BOM):文件、协议、数据库、终端,一个字符集走到底是最省心的架构。
  • 每一跳显式声明:HTML meta、HTTP 头、MySQL 连接字符集;给 Excel 的 UTF-8 CSV 要带 BOM 它才认。
  • 二进制走 Base64 或二进制协议,别让原始字节混进文本通道。
  • 出问题时先问「哪层写的、哪层读的」,用 <a class="faq-link" href="/tools/encoding-fix/">编码修复工具</a>验证猜想,而不是硬编码 replace 补丁。

常见问题

「锟斤拷」还能修复吗?

多数情况不能。锟斤拷意味着数据先被 UTF-8 解码失败替换成了 U+FFFD——替换发生的那一刻,原始字节已被丢弃,信息真的丢了。只能从更上游(源文件、原始接口)重新获取。它能修的是「还没到 U+FFFD 那一步」的数据。

UTF-8 和 GBK 能互相无损转换吗?

不是对称的。GBK → UTF-8 基本无损(极个别生僻字除外),UTF-8 → GBK 会丢字符(emoji、生僻字变 ?)。迁移老系统时建议一次性转 UTF-8 并存档原始数据。

Linux 终端中文显示乱码怎么查?

三层检查:locale(LC_ALL/LANG 是否为 *.UTF-8)、终端模拟器的字符编码设置、文件本身的编码。大多数「本地文件正常、终端乱码」都是 locale 没设成 UTF-8;用 编码修复工具先确认文件编码再对症。

UTF-8 的 CSV 用 Excel 打开是乱码,怎么让 Excel 认?

给 CSV 文件开头加 UTF-8 BOM(EF BB BF)——Excel 只靠 BOM 识别编码。用编辑器「另存为 UTF-8 with BOM」,或导出时程序自动补三个字节。注意带 BOM 的文件喂给某些 Linux 工具时首个列名会带 \uFEFF,按需剥离。

📂

松开以自动格式化

支持 .json / .sql / .csv / .html / .css / .xml / .yaml 文件