fgetcsv是PHP原生安全读取CSV的首选函数,自动处理逗号、换行、引号等复杂字段;需注意BOM头、编码、分隔符设置及错误排查(用ferror而非仅判false)。

用 fgetcsv 逐行读取 CSV 文件最稳妥
PHP 原生处理 CSV,fgetcsv 是首选——它自动处理带逗号、换行、引号的字段,比 file() 或 explode 安全得多。直接用 str_getcsv 只适合单行字符串,不适用于文件流。
常见错误是没设好分隔符或没处理 BOM 头,导致首行乱码或字段偏移。
- 打开文件必须用
'r'模式,且建议加mb_internal_encoding('UTF-8')防止中文乱码 - 用
fgetcsv($handle, 0, ',', '"', '"'):第二个参数设为0表示不限制行长度(PHP 5.4+),避免长字段被截断 - 读取前先用
stream_filter_prepend($handle, 'convert.iconv.UTF-8/UTF-8//IGNORE')过滤非法字节(尤其 Windows 记事本保存的 CSV) - 如果 CSV 有标题行,用一次
fgetcsv跳过,再进循环
遇到 fgetcsv 返回 false 怎么排查
不是文件不存在,而是更隐蔽的问题:编码、BOM、权限、或文件指针已到 EOF。返回 false 并不等于报错,ferror($handle) 才能确认是否真出错。
- 先检查
is_readable($filepath)和file_exists($filepath),但这两个通过不代表能读——SELinux 或 open_basedir 限制也会静默失败 - 用
hexdump -C $filepath | head -n1看前几个字节,确认是否有 UTF-8 BOM(ef bb bf),有就用file_get_contents+mb_substr剔除再写回临时文件 -
fgetcsv在空行或全空白行可能返回false,实际应判=== false而非== false,避免把空数组当失败 - Windows 换行符
\r\n在某些 PHP 版本下会导致多读一行,可在fopen后加stream_set_read_buffer($handle, 0)减少缓冲干扰
大文件(>10MB)用 fgetcsv 会卡死?得加内存和超时控制
默认 PHP 内存限制和 max_execution_time 会中断读取,但问题不在函数本身,而在没做流式节制。
立即学习“PHP免费学习笔记(深入)”;
- 用
set_time_limit(0)解除脚本超时,但别在 web 环境用——CLI 下才安全 - 每读 1000 行执行一次
gc_collect_cycles(),防止内存持续增长(尤其字段含长文本时) - 不要把整张表存进一个数组,改用生成器:
yield每行数据,调用方按需消费 - 如果必须导入数据库,用
PDO::prepare+execute单条插入太慢,换成批量INSERT INTO ... VALUES (),(),(),每 500 行提交一次事务
用 str_getcsv 处理 CSV 字符串时要注意引号嵌套
str_getcsv 不读文件,只解析字符串,常用于 API 接收的 CSV 内容或拼接后的行。但它对双引号内含双引号(即 "a""b")的支持依赖 enclosure 参数,且 PHP 版本差异大。
- PHP 5.6+ 支持第四个参数
$escape(默认"\\"),但 CSV 标准用两个双引号转义,所以得手动替换:str_replace('""', '\\"', $line)再传入 - 不要用
str_getcsv($line, ',')硬切,字段含逗号时直接崩;必须指定enclosure = '"' - 如果字符串来自
file_get_contents,记得先trim($content)去首尾空白,否则首行可能多出不可见字符影响解析 - 测试时用真实数据片段,比如
"姓名","年龄","备注"\n"张三","25","喜欢""Python""和PHP",看是否正确还原为["张三","25","喜欢\"Python\"和PHP"]
fgetcsv 会跨行合并,这种错误不会报异常,只会让后续逻辑全偏移。











