本文目录导读:

- 方案一:给文件添加 UTF-8 BOM(最推荐)
- 方案二:转换为 GBK/GB2312 编码(传统方法)
- 方案三:前置
<meta>标签(仅用于导出后HTML预览) - 方案四:使用 CSV 注入防御(安全补充)
- 总结与排查步骤
PHP导出CSV出现乱码,最常见的原因是编码不一致,通常是因为CSV文件是UTF-8编码,而Excel打开时默认使用了GBK(或ANSI)编码,BOM头缺失也可能导致Excel识别错误。
以下是几种最有效的解决方案,按推荐程度排序:
给文件添加 UTF-8 BOM(最推荐)
这是解决Excel打开乱码最简单直接的方法,BOM(字节顺序标记)是文件开头的一小段二进制数据,Excel看到它能立刻识别出这是UTF-8编码。
操作方式: 在输出CSV内容之前,先输出一个 \xEF\xBB\xBF。
<?php
// 设置输出头
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="data.csv"');
// 关键步骤:先输出 BOM 头,防止 Excel 打开乱码
echo "\xEF\xBB\xBF";
// 打开输出流
$output = fopen('php://output', 'w');
// 添加表头
fputcsv($output, ['姓名', '年龄', '城市']);
// 添加数据
fputcsv($output, ['张三', 25, '北京']);
fputcsv($output, ['李四', 30, '上海']);
fclose($output);
exit;
?>
缺点: 如果该文件要用于程序化导入(如某些数据平台),BOM头可能会成为干扰,导致第一列字段名带有多余字符。
转换为 GBK/GB2312 编码(传统方法)
由于Excel在中文Windows系统下默认使用GBK,可以将数据直接转换成GBK输出。
操作方式: 使用 mb_convert_encoding 或 iconv。
<?php
header('Content-Type: text/csv; charset=GBK');
header('Content-Disposition: attachment; filename="data.csv"');
$output = fopen('php://output', 'w');
// 表头也要转换
fputcsv($output, ['姓名', '年龄']);
// 注意:需要将每一行数据都转成 GBK
$data = ['张三', 25];
// 遍历数组转换编码
$data_gbk = array_map(function($item) {
return mb_convert_encoding($item, 'GBK', 'UTF-8');
}, $data);
fputcsv($output, $data_gbk);
fclose($output);
?>
缺点: 代码略显繁琐,需要手动对每个字段进行转换。
前置 <meta> 标签(仅用于导出后HTML预览)
如果你是通过浏览器直接点击生成的CSV,且没有设置 Content-Type: text/csv,而是直接输出文本,可以在输出内容最前面加一个meta标签。
<?php
// 注意此时不要设置 Content-Type 为 text/csv
header('Content-Type: text/html; charset=UTF-8');
echo '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">';
// 然后输出表格内容... (通常这种方式适用于导出 xls 格式)
?>
这种方法较少用于 .csv,常用于 .xls 导出。
使用 CSV 注入防御(安全补充)
如果你发现乱码伴随的是 , , , 等符号,可能是Excel公式注入问题,建议对字段进行清理:
function sanitize_csv_field($field) {
$field = mb_convert_encoding($field, 'GBK', 'UTF-8'); // 或者保留UTF-8
if (in_array(substr($field, 0, 1), ['=', '+', '-', '@', "\t", "\r"])) {
$field = "'" . $field;
}
return $field;
}
总结与排查步骤
-
先用记事本打开生成的CSV文件:
- 如果记事本打开正常,说明文件内容是UTF-8,是Excel识别问题 → 用方案一(加BOM)。
- 如果记事本打开也是乱码,说明文件内容本身是GBK,或者写入时数据损坏 → 检查数据库连接编码是否为UTF-8,或使用方案二强制转换。
-
检查数据库读取:
- 确保
PDO或mysqli连接设置了SET NAMES utf8mb4,否则数据在内存中就已经是乱码了。
- 确保
-
代码更新:建议优先尝试方案一,通常一行代码即可解决大部分乱码问题。