fgetcsv读csv前需手动处理bom、设对编码和文件模式;必须显式指定分隔符、封装符、转义符;勿省略长度参数;空行注释需自行跳过;应流式读取避免内存溢出。

用 fgetcsv 读 CSV 前必须设对文件指针和编码
php 默认不处理 BOM 和中文编码,直接 fopen + fgetcsv 读 UTF-8 带 BOM 的 CSV,第一行字段名常乱码或错位。这不是 fgetcsv 的 bug,是流底层没跳过 BOM、也没指定输入编码。
- 打开文件前加
stream_filter_append($fp, 'convert.iconv.UTF-8/UTF-8//IGNORE')(仅 PHP 7.4+ 支持)或手动检测并跳过 BOM 字节 - 确保文件以
rb模式打开:fopen('data.csv', 'rb')—— 文本模式(r)在 windows 下可能把rn错当n截断字段 - 如果 CSV 来自 excel 或网页导出,大概率含 BOM;用
hexdump -C data.csv | head -n1看前 3 字节是不是ef bb bf
fgetcsv 的分隔符、封装符、转义符不是“自动猜”的
很多人以为 fgetcsv 能智能识别不同格式的 CSV,其实它只按你传的参数切——参数不对,数据就裂开。尤其遇到字段里含逗号、换行或双引号时,不显式指定封装符(")和转义符( 或 "),fgetcsv 会直接截断或报错。
- 最安全调用:
fgetcsv($fp, 0, ',', '"', '"')—— 第三个参数是分隔符,第四个是封装符,第五个是转义符(PHP 5.3+ 支持第五个参数) - Excel 导出的 CSV 通常用双引号封装,且内部双引号写成
"",所以转义符也得设成",否则第二层引号会被吃掉 - 别省略第二个参数(长度限制):设成
0表示不限长;设太小(如1024)会导致长字段被截断,且后续行全偏移
空行、注释行、表头跳过要自己控制,fgetcsv 不管这些
fgetcsv 只负责按 CSV 规则解析一行,不会帮你过滤空行、跳过以 # 开头的注释、或识别哪行是 header。实际导入时,这些逻辑漏掉就会导致数据错位或插入失败。
- 读每行前先
$line = fgets($fp)判断是否为空或注释:trim($line) === '' || strpos(trim($line), '#') === 0 - 表头处理建议单独读一次:
$header = fgetcsv($fp, 0, ',', '"', '"'),之后循环读数据行,用array_combine($header, $row)构建关联数组 - 注意:如果 CSV 第一行是空行,
fgetcsv返回false,但文件指针已移动,后续读取会整体下移——务必在fgetcsv前检查返回值
内存和大文件:别一次性 file_get_contents 再 str_getcsv
有人图省事用 file_get_contents 把整个 CSV 读进内存,再用 str_getcsv 拆——这在几 MB 还行,到 50MB+ 就直接 OOM。而 fgetcsv 是流式读取,只要单行不超内存,整体可处理 GB 级文件。
立即学习“PHP免费学习笔记(深入)”;
- 避免
file('data.csv')或file_get_contents+explode("n")—— 这些不识别 CSV 封装规则,遇到换行字段就崩 - 真要批量导入,建议配合
set_time_limit(0)和gc_disable()(处理完再gc_enable()),防止超时或频繁 GC 拖慢 - 如果字段含大量 HTML 或 Base64,记得在入库前做
trim()和htmlspecialchars_decode(),否则视觉上“空”其实是空白符或实体符
CSV 解析真正难的不是语法,是那些藏在 Excel 保存选项里、http 响应头里、甚至编辑器自动加的不可见字符——它们不会报错,只会让第 372 行的邮箱字段少一个 @ 符号。