str_getcsv()是解析csv的首选方案,因其采用状态机逐字符解析,能正确处理字段内逗号、换行及双引号转义(如""→"),支持自定义分隔符与封装符,且兼容rfc 4180规范。

为什么直接用 explode() 或简单正则会崩
因为 CSV 字段里可能含逗号、换行,甚至双引号本身。比如 "a,b",c,"d""e":
- explode(',', $s) 会把 "a,b" 拆成两段
- /[^,]+/ 会把 "d""e" 截断在第一个 " 后
- 更糟的是,"" 是转义双引号(RFC 4180 规范),不是字段结束标记
关键矛盾在于:你得区分「作为边界用的双引号」和「作为内容用的双引号」,而后者总是以成对形式 "" 出现。
str_getcsv() 是首选,不是“可选”
PHP 自带的 str_getcsv() 就是专为这个场景设计的:
- 自动识别双引号包裹的字段
- 正确还原 "" → "
- 支持自定义分隔符、封装符、转义符(如 str_getcsv($line, ';', "'", '\'))
- 能处理字段内换行(只要符合 RFC 4180,即换行在引号内)
它不依赖正则,而是用状态机逐字符解析,比任何正则都稳。除非你明确不能用内置函数(比如要提取所有引号块但不按字段顺序),否则别绕开它。
硬要用正则时,必须匹配结构而非分割
别试图用 split 思路写正则。正确做法是用 preg_match_all() 匹配每个完整字段单元:
- 推荐模式:/"((?:[^"]|"")*)"|([^",
]*)/
- 前半部分匹配带引号字段:"((?:[^"]|"")*)",其中 (?:[^"]|"")* 表示「非引号字符 or 两个连续引号」,非捕获,避免嵌套干扰
- 后半部分匹配无引号字段:([^",
]*),停在逗号、引号或换行前
- 实际使用时需注意:
- 匹配结果在
$matches[0]是整个字段(含引号),$matches[1]和$matches[2]才是内容,需判空取有效组 - 字段间可能有空格,模式开头加
s*,结尾加(?:s*,s*)?更鲁棒 - 该正则无法处理跨行字段(即引号内换行),
str_getcsv()可以,正则不行
手写状态机才是终极兼容方案
当输入不守 RFC(比如引号没闭合、混用单双引号、字段间空白不规范),连 str_getcsv() 都可能失败。这时必须自己遍历字符串,维护两个状态:
- in_quote:是否处于双引号内
- last_was_quote:上一个字符是不是引号(用于判断 "" 是否构成转义)
遇到 " 时:
- 若
!in_quote→ 进入引号区 - 若
in_quote && next char is '"'→ 跳过下一个引号,当前视为转义 - 若
in_quote && next char not '"'→ 退出引号区
遇到,且!in_quote→ 字段切分点
这逻辑看着琐碎,但它是唯一能覆盖所有畸形 CSV 的方式。别信“一行正则搞定”,那只是对标准数据的幻觉。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











