PHP 全局匹配的结果组织方式
全局匹配函数用标志决定返回结果的形状:默认按组聚合,即先给整体匹配的全部取值,再给第一组的全部取值依次排列;另一种标志改为按匹配聚合,把每一次匹配的所有字段打包在一起。选错标志不会报错,只会让取值代码拿到另一个维度的数据,这是使用这些函数时最典型的错误。
基本信息
| 默认标志 | 按组聚合:下标 0 是全部整体匹配,下标 1 是第一组的全部取值,依此类推 |
|---|---|
| 按匹配聚合 | 把每次匹配的字段打包,下标 0 是第一次匹配的完整信息 |
| 偏移信息 | 附带偏移标志后,每个值变成内容与字节偏移两项 |
| 空值区分 | 未参与匹配的组默认是空字符串,可用专门标志改为空值以便区分 |
典型使用场景
- 统计字段分布:按组聚合时可以直接对某一列做计数与聚合
- 逐条记录处理:按匹配聚合便于把每次匹配当作一条记录写入数据库
- 高亮与替换:需要位置信息时附带偏移标志,取出内容与字节位置
- 区分缺失与空值:需要明确某个可选字段是否出现时使用空值标志
- 批量校验:一次匹配全部结果,减少重复调用带来的开销
两种组织方式实测
两种组织方式实测
$s = 'a-1 b-2';
$n = preg_match_all('/(\w+)-(\d+)/', $s, $m); // 默认按组聚合,返回 2
$m[0]; // ['a-1', 'b-2']:全部整体匹配
$m[1]; // ['a', 'b']:第一组的全部取值
preg_match_all('/(\w+)-(\d+)/', $s, $s2, PREG_SET_ORDER); // 按匹配聚合
$s2[0]; // ['a-1', 'a', '1']:第一次匹配的完整字段
preg_match_all('/(a)?(b)/', 'b', $m3, PREG_UNMATCHED_AS_NULL); // 未参与组为 null
preg_match_all('/\w/', $s, $m4, PREG_OFFSET_CAPTURE); // 附带字节偏移
$m4[0][0]; // ['a', 0]:内容与起始字节位置结果结构对照
| 标志 | 结构形态 | 取值方式 |
|---|---|---|
| 按组聚合(默认) | 第一维是组号,第二维是匹配序号 | 先选字段再遍历匹配 |
| 按匹配聚合 | 第一维是匹配序号,第二维是组号 | 先取一次匹配再取字段 |
| 附带偏移 | 每个值为内容与偏移两项 | 需要同时读内容和位置 |
| 空值标志 | 未参与组为 null | 区分缺失与匹配到空内容 |
偏移量的单位是字节而不是字符:处理多字节文本时用字符索引函数会错位,需要截取上下文时要么按字节处理,要么重新计算字符位置。
两个排序标志不应同时指定,混用时结果结构不可预期;确定取值代码的写法后,把所用标志写进函数调用的注释里,能显著减少后续维护中的误改。