PHP如何处理中文乱码_PHP避坑指南【编码】
PHP中文乱码问题常见,根源在于文件编码、HTTP头、数据库连接及HTML声明未统一为UTF-8。文件必须存为UTF-8无BOM格式;数据库连接需用mysqli_set_charset函数设字符集为utf8mb4,避免使用SETNAMES;读取GBK文件需转换编码;CSV导出添加BOM可防Excel乱码。任一环节出错即乱码。
PHP中文乱码这事儿,说到底就是一整个链条上的编码对齐问题,缺一环都不行。很多人以为加个header()就万事大吉,其实远远不够。真正需要严格对齐的是四个层面:PHP文件本身的编码、HTTP响应头、数据库连接,以及HTML页面的声明。只要其中有一层用了GBK、ISO-8859-1或者那个带BOM的UTF-8,就算你其他三层都设成UTF-8,照样白搭。
PHP文件保存为UTF-8无BOM是前提
BOM这个东西,看着不起眼,实则是个隐形杀手。\xEF\xBB\xBF这三个字节在你执行header()之前就偷偷输出了,直接触发“headers already sent”错误,后果就是session_start()报错、JSON解析失败。更隐蔽的是,很多编辑器默认存成“UTF-8 with BOM”,尤其Windows下的Notepad++和旧版VS Code,这点特别容易踩坑。
- VS Code:右下角点编码 → “Sa ve 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')+ - 避免用
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()和,却不知道编辑器悄悄把文件存成了GBK,或者以为建了utf8mb4的表就万事大吉,结果连接层还是latin1。乱码问题从来不是单点故障,而是链路中任意一环断裂的结果。


































