mb_convert_encoding乱码是因为只重解释字节而不识别原始编码;需先用mb_detect_encoding精准判断源编码,再显式指定源和目标编码转换。

mb_convert_encoding 转编码时为什么还是乱码? 说明:这不是函数没起作用,而是它只做「字节重解释」,不识别原始编码。你给错源编码,结果必然错。
实操建议:
- 用 mb_detect_encoding($str, ['UTF-8', 'GBK', 'BIG5'], true) 先猜(注意第三个参数 true 表示 strict 模式,避免误判)
- 猜不准就看来源:MySQL 默认是 latin1(但存中文其实是 GBK 或 UTF8MB4),前端 POST 过来一般是 UTF-8,文件本身用 file -i filename.php 查
- 转换时务必写全目标和源编码:mb_convert_encoding($str, 'UTF-8', 'GBK'),漏掉第三个参数会默认用 mb_internal_encoding() 值,常埋雷
- 不要链式调用:mb_convert_encoding(mb_convert_encoding($str, 'UTF-8'), 'GBK')——这是在乱转,不是“反复矫正”
iconv 函数报 “Illegal character” 错误怎么处理?
说明:iconv 对非法字节零容忍,遇到无法解析的 byte sequence 直接失败;而 mb_convert_encoding 默认会跳过或替换,更“宽容”。
实操建议:
- 加 //IGNORE 或 //TRANSLIT 后缀:iconv('GBK', 'UTF-8//IGNORE', $str)
- //IGNORE 丢弃非法字符,//TRANSLIT 尝试音近替换(如把 ① 转成 (1)),但对中文基本无效
- 注意:PHP 8.2+ 已废弃 iconv 的某些别名(如 CP936 建议显式写 GBK),否则可能触发 warning
- 如果数据来自老旧 Windows 记事本保存的 ANSI 文件,大概率是 GBK,不是 UTF-8 with BOM——BOM 是 \xEF\xBB\xBF,可用 bin2hex(substr($str, 0, 3)) === 'efbbbf' 判断
数据库读出来是乱码,但 phpMyAdmin 显示正常
说明:不是数据坏了,是 PHP 连接层没告诉 MySQL “我要 UTF-8”,导致 MySQL 把字段当 latin1 发出来,PHP 再按 UTF-8 解就糊了。
实操建议:
- MySQLi 连接后立刻执行:$mysqli->set_charset('utf8mb4')(别用 utf8,那是 MySQL 的残缺实现)
- PDO 构造时加 DSN 参数:charset=utf8mb4,例如 mysql:host=localhost;dbname=test;charset=utf8mb4
- 检查表结构:SHOW CREATE TABLE t,确认字段是 CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci,光连上 utf8mb4 不够,存的时候也得是
- 不要用 SET NAMES latin1 或类似语句覆盖连接编码,尤其在框架里被中间件悄悄执行了
header('Content-Type: text/html; charset=UTF-8') 没用 说明:HTTP 响应头只是告诉浏览器“我发的是 UTF-8”,但如果 PHP 文件自己存的是 GBK 编码,那发出去的字节流根本不是 UTF-8,浏览器再怎么信也没用。
实操建议:
- 用编辑器确认 PHP 源文件编码:VS Code 右下角、Sublime 右下角都显示当前编码,必须是 UTF-8(无 BOM)
- echo 出来的字符串,如果含中文,必须是 UTF-8 字节序列——验证方式:mb_check_encoding($str, 'UTF-8') 返回 true
- Apache 用户检查 AddDefaultCharset 是否被设为 GBK,Nginx 用户确认没有 charset GBK 这类配置覆盖了你的 header
- 浏览器开发者工具 Network → Response Headers 看实际发出的 Content-Type,别只信代码里写了什么
乱码问题最麻烦的从来不是函数不会用,而是你不知道那一串字节到底本来是什么编码、在哪个环节被错误解释了一次。多打几个 bin2hex() 和 mb_detect_encoding(),比翻文档快。











