乱码

由于文本数据的编码与解码方式不一致,导致原本的字符显示为无意义的符号或其他字符的现象。

乱码 (mojibake) 是指写入文本时使用的字符编码与读取时使用的编码不匹配而产生的显示异常。例如,将以 UTF-8 保存的中文文本用 GBK 打开,「你好世界」可能变成「浣犲ソ涓栫晫」之类的乱码;反过来,用 UTF-8 打开 GBK 编码的文件,则会出现大量「�」(替换字符) 或完全不相关的汉字。

同一串字节如何变成不同的字符 - 写入方与读取方的约定不一致
  1. 1. 写入方以 UTF-8 保存 「你好世界」这 4 个字符在 UTF-8 中各占 3 个字节,因此文件里保存的是一串共 12 个字节的数据。
  2. 「这是 UTF-8」这个约定没有传达给读取方
  3. 2. 读取方按 GBK 的规则切分字节 同样的 12 个字节被按 GBK 的规则重新切分,字符的边界与原来的 4 个字符完全不同。
  4. 查到的是另一张编码表
  5. 3. 屏幕上出现「浣犲ソ涓栫晫」 改变的只是显示结果,原本的 4 个字符并没有被替换成别的数据,问题在于读取时指定的编码不对。
保存时使用的编码读取时使用的编码屏幕上出现的字符串
UTF-8UTF-8 (一致)你好世界
UTF-8GBK浣犲ソ涓栫晫
UTF-8Latin-1出现无法阅读的符号,4 个字符被数成 12 个字符
GBKUTF-8出现大量替换字符 �,或完全不相关的汉字

4 种组合中,只有保存与读取使用同一编码的那一行能正常显示。在统计字符数之前,请先确认读取时使用的编码与保存时是否一致。

乱码发生的典型场景有三种。第一,文件保存时与读取时的编码不同;第二,数据库的连接字符集与表的字符集不一致;第三,HTTP 响应头中 Content-Type 指定的 charset 与 HTML 文件的实际编码不同。这三种情况的本质相同 - 写入方和读取方对编码的「约定」没有对齐。

从历史角度看,乱码问题在东亚地区尤为突出。中文环境中,GB2312、GBK、GB18030、Big5 等多种编码长期并存。大陆使用 GB 系列编码,台湾和香港使用 Big5,日本则有 JIS、Shift_JIS、EUC-JP 三种主要编码。在电子邮件和早期网页中,由于编码声明缺失或不正确,乱码几乎是家常便饭。

如今 UTF-8 已成为 Web 的事实标准,乱码的发生频率大幅降低。根据 W3Techs 的使用情况调查,截至 2026 年 9 月 5 日,99.0% 的网站采用 UTF-8 编码。然而,在与遗留系统对接、CSV 文件交换 (Excel 默认期望带 BOM 的 UTF-8)、旧数据库迁移等场景中,乱码问题并未完全消失。

防止乱码的实务要点非常明确:文件统一使用 UTF-8 (无 BOM) 保存;数据库字符集使用 utf8mb4;HTTP 响应明确声明 Content-Type: text/html; charset=UTF-8;需要用 Excel 打开的 CSV 文件则输出为带 BOM 的 UTF-8。贯彻这些原则,就能让写入方与读取方使用同一编码,而这正是避免乱码的前提。

从字符计数的角度来看,乱码文本的可见字符数与实际字节数会严重偏离。例如,将 UTF-8 编码的中文文本按 Latin-1 解释,一个汉字会膨胀为 3 个字符,导致计数结果接近实际值的 3 倍。因此,准确的字符计数的前提是文本编码被正确识别。

分享这篇文章