
php 导出 csv 时中文显示为问号(???),通常是编码未正确声明或缺少 utf-8 bom 头导致;本文详解通过添加 bom 标头、设置响应头及正确使用 fputcsv 解决中文乱码问题。
php 导出 csv 时中文显示为问号(???),通常是编码未正确声明或缺少 utf-8 bom 头导致;本文详解通过添加 bom 标头、设置响应头及正确使用 fputcsv 解决中文乱码问题。
在 PHP 中导出包含中文字符的 CSV 文件,常遇到 Excel 打开后中文显示为 ??? 的问题——这并非数据本身损坏,而是 Excel 缺乏明确的编码提示,导致其默认按 ANSI(如 GBK)解析 UTF-8 编码的文本。核心解决方案是:显式声明 UTF-8 编码 + 添加 UTF-8 BOM(Byte Order Mark)头。
✅ 正确做法三要素
-
设置正确的 HTTP 响应头
Content-Type必须指定charset=UTF-8,且语法需规范(冒号后无空格,charset是参数,非 MIME 类型子类型):header('Content-Type: text/csv; charset=UTF-8'); header('Content-Disposition: attachment; filename="' . $filename . '.csv"'); // 注意:无需设置 Content-Encoding,UTF-8 不是传输编码 -
输出 UTF-8 BOM(关键!)
BOM(0xEF 0xBB 0xBF)是 Excel 识别 UTF-8 文件的“信号”。它必须写入输出流最开头,且仅一次。使用fopen('php://output', 'w')后,立即写入 BOM:$file = fopen('php://output', 'w'); fwrite($file, "\xEF\xBB\xBF"); // 或 fprintf($file, "%s", "\xEF\xBB\xBF"); 确保数据本身为 UTF-8 编码
若数据库字段为utf8mb4且连接已设SET NAMES utf8mb4,则mysqli_fetch_array()返回的中文默认为 UTF-8,可直接使用。否则需用mb_convert_encoding()转换。
✅ 完整安全示例代码
<?php // 假设已建立 UTF-8 兼容的 mysqli 连接
$code = (int)$_POST['code'];
$year = preg_replace('/[^0-9]/', '', $_POST['year']); // 防注入 & 清洗
$a = mysqli_query($db, "SELECT * FROM remark_type WHERE remark_code = ?");
$stmt = mysqli_prepare($db, "SELECT * FROM remark_type WHERE remark_code = ?");
mysqli_stmt_bind_param($stmt, "i", $code);
mysqli_stmt_execute($stmt);
$getrow = mysqli_fetch_array($stmt);
if (!$getrow) {
die('No data found');
}
// 构造安全文件名(避免特殊字符)
$filename = mb_substr($getrow["title"], 0, 50, 'UTF-8') . "_{$year}";
$filename = preg_replace('/[^\w\-_\. ]/', '_', $filename); // 替换非法字符
// 设置响应头
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="' . $filename . '.csv"');
header('Cache-Control: no-cache');
header('Pragma: no-cache');
// 输出 BOM(必须在任何内容之前)
echo "\xEF\xBB\xBF";
// 写入 CSV 数据
$file = fopen('php://output', 'w');
// 示例:写入表头(中文列名)
fputcsv($file, ['序号', '备注类型', '代码', '创建时间']);
// 写入数据行($newname 应为二维数组,每行为关联/索引数组)
foreach ($newname as $line) {
// 确保每项为字符串且 UTF-8 安全(fputcsv 会自动转义,无需额外处理)
fputcsv($file, $line);
}
fclose($file);
exit;
?>
⚠️ 注意事项与常见错误
- ❌ 错误:
header('Content-type: application/csv;charset:UTF-8')——application/csv不是标准 MIME 类型,应使用text/csv。 - ❌ 错误:BOM 写在
fputcsv()之后,或重复写入 —— BOM 必须位于文件绝对开头,且仅一次。 - ❌ 错误:PHP 文件自身编码不是 UTF-8(无 BOM)——请用编辑器(如 VS Code)将
.php文件保存为 UTF-8 无 BOM 格式。 - ✅ 推荐:使用预处理语句防止 SQL 注入(如上例),避免直接拼接
$_POST["code"]。 - ✅ 验证:导出后用记事本打开 → “另存为”查看编码是否为 UTF-8;或用
file -i filename.csv(Linux/macOS)确认。
遵循以上步骤,即可稳定导出 Excel 可正确识别中文的 CSV 文件。本质是让客户端(尤其是 Excel)明确知道:“这是 UTF-8 编码,请勿猜测”。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











