CSV乱码主因是Excel保存为GBK而PHP按UTF-8解析,解决需读取后立即用iconv('GBK','UTF-8//TRANSLIT//IGNORE')逐字段转换,并校验文件头与系统GBK支持。

CSV 文件用 Excel 保存时默认是 GBK 编码,但 PHP 读取时按 UTF-8 解析就会乱码
绝大多数乱码问题根源在这里:Windows 上用 Excel 保存的 CSV 文件,实际是 GBK(或 GB2312)编码,而 PHP 脚本本身、Web 页面、数据库连接通常设为 UTF-8。直接用 fgetcsv() 或 file_get_contents() 读取,字节流没做转换,浏览器一渲染就出现“æäº›æå”这类典型 UTF-8 解释 GBK 字节的乱码。
解决思路不是改 Excel 设置(用户不可控),而是读取后立刻做编码转换。优先用 iconv(),它比 mb_convert_encoding() 更轻量、更稳定,尤其对 GBK → UTF-8 这类常见转换容错更强。
用 iconv() 转换前必须确认原始编码,不能盲目写 'GBK'
直接硬写 iconv('GBK', 'UTF-8//IGNORE', $line) 很危险——如果文件其实是 GB2312、GB18030,甚至某些 Excel 导出带 BOM 的 UTF-8,iconv() 会失败并返回 false,后续逻辑可能崩掉。
建议步骤:
立即学习“PHP免费学习笔记(深入)”;
- 先用
mb_detect_encoding()粗略判断,但别全信——它对短文本、纯英文 CSV 准确率低 - 更可靠的是检查文件头:读取前 3 字节,如果是
\xEF\xBB\xBF就是 UTF-8 BOM,跳过;否则按 Windows 默认行为,优先尝试GBK - 加一层
iconv()的错误兜底:iconv('GBK', 'UTF-8//TRANSLIT//IGNORE', $line)中的//TRANSLIT能把无法映射的字符转成近似 ASCII(如 “é” → “e”),避免整行丢弃
fgetcsv() 读取后立即 iconv,别等全部读完再批量转
CSV 行与行之间编码一致,但每行内容长度差异大,延迟转换容易在内存中混入未解码数据,后续 JSON 输出或入库时又触发二次乱码。必须在每次 fgetcsv() 返回数组后,立刻遍历字段做转换。
使用 qbo-mileage CLI 及用户凭证,从 Airtable、Outlook 或 Google Calendar 记录生成 QuickBooks Online 里程 CSV 文件。
示例关键片段:
$handle = fopen($filepath, 'r');
while (($row = fgetcsv($handle)) !== false) {
// 每个字段单独转,防止某列含特殊符号导致整行失败
$clean_row = array_map(function($cell) {
return iconv('GBK', 'UTF-8//TRANSLIT//IGNORE', $cell);
}, $row);
// 后续处理 $clean_row,此时已是干净 UTF-8
}
fclose($handle);
注意:array_map() 里不要用引用或全局变量传编码名——万一某次上传的是 UTF-8 文件,硬写死 'GBK' 会导致所有文字变空。生产环境应把编码作为参数传入回调,或封装成可配置函数。
Linux 服务器上 iconv 可能不支持 GBK,需检查系统 locale
部分精简版 Linux(如 Alpine)默认不带 GBK 支持,iconv -l | grep -i gbk 查不到结果,调用 iconv('GBK', ...) 会直接报 Unknown encoding 错误。
临时方案:
- 改用
mb_convert_encoding($cell, 'UTF-8', 'GBK'),它依赖 PHP 的 mbstring 扩展,只要扩展启用,编码列表更全 - 长期建议:Docker 中安装
glibc-bin(Alpine)或locales(Debian/Ubuntu),并生成zh_CN.GBKlocale - 验证方式:运行
php -r "var_dump(iconv('GBK', 'UTF-8', '测试'));",有输出说明可用
真正麻烦的不是转换逻辑,而是同一套代码在开发机(Windows + 完整 iconv)和线上(容器 + 最小化系统)行为不一致。上线前务必用真实 CSV 文件跑通全流程,别只测 echo。


















