preg_match需加preg_offset_capture标志才能获取字节偏移量,结果为[$value, $offset]二维结构;多匹配用preg_match_all同理;utf-8下字节偏移≠字符位置,需转换。

preg_match 本身不返回位置,要用 PREG_OFFSET_CAPTURE
直接调用 preg_match 默认只返回匹配内容,不带位置信息。想拿到起始偏移量,必须传入 PREG_OFFSET_CAPTURE 标志位,并且结果结构会变成二维数组:每个匹配项是 [值, 偏移量] 形式。
常见错误是忘了加这个标志,或者加了但没处理嵌套数组结构,导致 Notice: Array to string conversion 或取错字段。
- 必须在第 4 个参数(
$matches)之后传PREG_OFFSET_CAPTURE -
$matches[0]是完整匹配,$matches[0][0]是字符串,$matches[0][1]是起始位置(字节偏移) - 子组捕获(如
(\d+))也会按同样结构存进$matches[1]、$matches[2]等
$text = 'abc123def';
preg_match('/(\d+)/', $text, $matches, PREG_OFFSET_CAPTURE);
// $matches[1] = ['123', 3] → 字符串在位置 3 开始
preg_match_all 才适合批量获取所有匹配位置
如果目标文本里有多个匹配项,preg_match 只能拿到第一个,必须换用 preg_match_all 并带上 PREG_OFFSET_CAPTURE。它会把全部匹配(含子组)按行组织,每行都是 [值, 偏移] 数组。
注意:preg_match_all 的返回值是匹配总数(int),不是 $matches;$matches 是输出参数,结构比 preg_match 更深一层。
- 完整匹配的位置在
$matches[0][$i][1],其中$i是第几个匹配 - 第一个捕获组的位置在
$matches[1][$i][1] - 不加
PREG_OFFSET_CAPTURE,$matches[0][$i]就只是字符串,没有位置
$text = 'a1b2c3';
preg_match_all('/(\d)/', $text, $matches, PREG_OFFSET_CAPTURE);
// $matches[0][0] = ['1', 1], $matches[0][1] = ['2', 3], $matches[0][2] = ['3', 5]
中文或 UTF-8 文本要小心字节偏移 vs 字符偏移
PREG_OFFSET_CAPTURE 返回的是字节偏移(byte offset),不是 Unicode 字符位置。对纯 ASCII 没影响,但遇到中文、emoji 时,一个汉字占 3 字节,substr($text, $pos, 1) 可能截出乱码。
如果业务逻辑依赖“第几个字符”,不能直接用这个偏移做 substr 或 mb_substr 的第二个参数——得先用 mb_strlen(substr($text, 0, $pos), 'UTF-8') 换算,或改用 mb_ereg(已废弃)或 preg_match 配合 u 修饰符 + 手动统计字符数。
-
u修饰符让 PCRE 支持 UTF-8,但不改变偏移单位,仍是字节 - 用
mb_strcut($text, $pos, 1, 'UTF-8')安全截取单字符,但性能略低 - 若只需判断位置关系(比如高亮),字节偏移足够;若需插入/删除字符,必须转为字符索引
替代方案:用 preg_match_all + PREG_PATTERN_ORDER 更易读?
有人觉得 PREG_OFFSET_CAPTURE 返回结构太深,想先拿到所有匹配字符串,再用 strpos 找位置。这在简单场景可行,但有严重缺陷:重复内容会导致 strpos 找错实例,正则里的量词、回溯、非贪婪等特性也让手动定位不可靠。
真正安全的方式只有原生支持位置的接口。如果实在不想处理嵌套数组,可以封装一层:
function preg_match_with_offset($pattern, $subject, &$match = null) {
if (preg_match($pattern, $subject, $raw, PREG_OFFSET_CAPTURE)) {
$match = [
'text' => $raw[0][0],
'offset' => $raw[0][1],
'length' => strlen($raw[0][0])
];
return true;
}
return false;
}
这种封装只适用于单次匹配;多次匹配仍得回到 preg_match_all + PREG_OFFSET_CAPTURE。
最常被忽略的是:偏移量是相对于整个字符串的,不是相对于上一个匹配;而且 $matches 是引用传入,漏写 & 会导致空数组。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











