php中文乱码需四层编码严格对齐:php文件(utf-8无bom)、http响应头(header('content-type: text/html; charset=utf-8'))、数据库连接(mysqli_set_charset('utf8mb4')或pdo dsn加charset=utf8mb4)、html声明(),任一层错配(如gbk、iso-8859-1或utf-8 with bom)即导致乱码。

PHP中文乱码不是加个header()就能解决的事,核心是四层编码必须严格对齐:PHP文件本身、HTTP响应头、数据库连接、HTML声明。只要其中一层是GBK、ISO-8859-1或UTF-8 with BOM,其他三层全设成UTF-8也没用。
PHP文件保存为UTF-8无BOM是前提
BOM(\xEF\xBB\xBF)是隐形杀手,它会在header()执行前偷偷输出三个字节,直接触发“headers already sent”错误,还会让JSON解析失败、session_start()报错。很多编辑器默认存成“UTF-8 with BOM”,尤其是Windows下的Notepad++和旧版VS Code。
- VS Code:右下角点击编码 → “Save with Encoding” → 选
UTF-8(不是UTF-8 with BOM) - Notepad++:菜单栏“编码” → “转为 UTF-8 编码”(避开带BOM的选项)
- Linux命令行验证:
file -i your.php,输出必须含charset=utf-8且不含bom - 批量清理BOM:
sed -i '1s/^\xEF\xBB\xBF//' *.php(慎用,先备份)
mysqli_set_charset('utf8mb4')不能写成SET NAMES utf8
MySQL的utf8不是真正的UTF-8,它只支持最多3字节字符,emoji和部分生僻汉字会被截断或变成问号。utf8mb4才是完整实现。更关键的是,mysqli_query($conn, "SET NAMES utf8")不等于mysqli_set_charset($conn, 'utf8mb4')——前者只是SQL语句,可能被连接池、中间件或并发干扰;后者直接修改客户端连接层的字符集,更可靠。
- 正确做法:
$mysqli->set_charset('utf8mb4'),必须在mysqli_connect()之后、任何查询之前调用 - PDO写法:DSN里必须显式加
;charset=utf8mb4,例如mysql:host=localhost;dbname=test;charset=utf8mb4 - 验证是否生效:
var_dump($mysqli->character_set_name())应返回utf8mb4 - 建表时字段也要指定:
VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
file_get_contents读中文文件要手动转码
file_get_contents()只读字节,不识别编码。如果源文件是GBK,而你的PHP脚本是UTF-8,直接echo就会乱码。它不会自动探测,也不会按BOM转换。
- 明确知道源编码时,用
iconv('GBK', 'UTF-8//IGNORE', $content),//IGNORE可跳过非法字符 - 不确定源编码但想试试,可用
mb_convert_encoding($content, 'UTF-8', 'auto'),但auto不可靠,仅限调试 - 读取后务必确保输出环境也是UTF-8:
header('Content-Type: text/html; charset=utf-8')+<meta charset="UTF-8"> - 避免用
fgets()逐行读GBK文件——换行符\n可能被误判为双字节的一部分,改用file()读整数组再转更稳
CSV导出中文要加BOM头\ufeff
Excel(尤其Windows版)根本不看HTTP的charset=utf-8,它默认用系统ANSI(通常是GBK)解码CSV。所以即使PHP输出的是标准UTF-8,Excel打开就是乱码。
- 解决方案:在CSV内容最前面加UTF-8 BOM:
$csv = "\xEF\xBB\xBF" . "姓名,城市\n张三,北京"; - 不要依赖
header('Content-Type: text/csv; charset=utf-8'),Excel会忽略它 - 如果数据来自MySQL,确认连接已设
utf8mb4,否则查出来的字段本身就是乱码字节 - CLI环境下写文件,乱码往往是因为终端编码不匹配,不是PHP的问题——先用
chcp 65001(Windows)或export LANG=en_US.UTF-8(Linux)统一终端编码
最容易被忽略的点是:PHP文件编码和数据库连接层这两处。很多人反复检查header()和<meta>,却忘了编辑器悄悄把文件存成了GBK,或者以为建了utf8mb4的表就万事大吉,结果连接层还是latin1。乱码问题从来不是单点故障,而是链路中任意一环断裂的结果。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











