PHP的preg_Match()返回最后一场比赛的位置

2022-08-08 00:00:00 regex php preg-match

preg_match($pattern, $subject, $matches, PREG_OFFSET_CAPTURE);

是否可以反向搜索字符串?即返回该模式在主题中最后一次出现的位置,类似于strripos

还是必须用preg_match_all返回所有匹配项的位置并使用$matches的最后一个元素?


解决方案

php没有从右向左搜索字符串的正则表达式方法(如.NET所示)。有几个可能的方法可以解决这个问题(这个列表不是详尽的,但它可能为您自己的解决方法提供一些想法):

  • 使用preg_match_allPREG_SET_ORDER标志和end($matches)将得到最后一个匹配集
  • 使用strrev反转字符串并构建要与preg_Match一起使用的反转模式
  • 使用preg_match并构建一个锚定在字符串末尾的模式,以确保在字符串末尾之前不再出现搜索到的掩码
  • 在目标之前使用贪婪的量词,K在您想要的位置开始匹配结果。到达字符串末尾后,正则表达式引擎将回溯,直到找到匹配项。

模式/x[A-Z]+d/的字符串$str = 'xxABC1xxxABC2xx'的示例

方法1:查找所有匹配项并显示最后一个匹配项。

if ( preg_match_all('/x[A-Z]+d/', $str, $matches, PREG_SET_ORDER) )
    print_r(end($matches)[0]);

Demo

方法2:查找颠倒的字符串与颠倒的模式的第一个匹配,并显示颠倒的结果。

if ( preg_match('/d[A-Z]+x/', strrev($str), $match) )
    print_r(strrev($match[0]));

Demo

请注意,反转模式并不总是那么容易。

方法3:从x跳到x,并检查字符串末尾是否没有其他x[A-Z]+d匹配项。

if ( preg_match('/x[A-Z]+d(?!.*x[A-Z]+d)/', $str, $match) )
    print_r($match[0]);

Demo

变体:

使用懒惰的量词

if ( preg_match('/x[A-Z]+d(?!.*?x[A-Z]+d)/', $str, $match) )
    print_r($match[0]);

或使用";缓和的量词

if ( preg_match('/x[A-Z]+d(?=(?:(?!x[A-Z]+d).)*$)/', $str, $match) )
    print_r($match[0]);

当您事先知道哪些地方最有可能发生匹配时,在这些变体之间进行选择可能会很有趣。

方法4:转到字符串末尾并回溯,直到找到x[A-Z]+d匹配。K从匹配结果中删除字符串的开头。

if ( preg_match('/^.*Kx[A-Z]+d/', $str, $match) )
    print_r($match[0]);

方法4(一种更手动的变体):为了限制回溯步骤,您可以贪婪地从字符串的开头开始前进,按原子组逐个原子组,并以相同的方式按原子组而不是按字符回溯。

if ( preg_match('/^(?>[^x]*Kx)+[A-Z]+d/', $str, $match) )
    print_r($match[0]);

相关文章