php preg_match返回上一场比赛的位置

时间:2014-04-28 13:57:54

标签: php regex preg-match

使用

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

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

或者我必须使用preg_match_all返回所有匹配的位置并使用$matches的最后一个元素吗?

4 个答案:

答案 0 :(得分:24)

PHP没有从右到左搜索字符串的正则表达式方法(如在.net中)。有几种可能的方法可以解决(这个列表并不详尽,但可能为您自己的解决方法提供了想法):

  • 使用preg_match_all PREG_SET_ORDER旗帜和end($matches)将为您提供最后一场比赛
  • 使用strrev反转字符串并构建要与preg_match一起使用的“反向”模式
  • 使用preg_match并构建一个锚定在字符串末尾的模式,以确保在字符串结束之前不再出现搜索到的掩码
  • 在目标前使用贪婪量词,\K在您想要的位置开始匹配结果。一旦到达字符串的末尾,正则表达式引擎将回溯直到找到匹配。

模式$str = 'xxABC1xxxABC2xx'

的字符串/x[A-Z]+\d/的示例

方式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

方式3(变体)非常相似,但这一次,它检查不再有x[A-Z]+\d次出现,但这一次,从当前位置到结束字符串。

使用惰性量词

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]);

当您事先知道匹配的概率最大时,可以选择这两种变体中的一种(方式3或方式3(变体)之一)。

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

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

方式4(更多手动驱动的变体)限制回溯步骤,你可以从字符串的开头贪婪地前进,原子组中的原子组,以及原子组的方式相同,而不是字符。

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

答案 1 :(得分:1)

我并不完全理解你想要什么,因为它取决于将捕获多少组,我根据组号创建了捕获最后一次捕获的偏移的函数,在我的模式中,有3组:第一组,完全捕获和另外两组,分组。

模式示例代码:

$pattern = "/<a[^\x3e]{0,}href=\x22([^\x22]*)\x22>([^\x3c]*)<\/a>/";

HTML示例代码:

$subject = '<ul>
<li>Search Engines</li>
<li><a href="https://www.google.com/">Google</a></li>
<li><a href="http://www.bing.com/">Bing</a></li>
<li><a href="https://duckduckgo.com/">DuckDuckGo</a></li>
</ul>';

我的函数,它捕获最后一个元素的偏移量,你可以指出匹配的数量:

function get_offset_last_match( $pattern, $subject, $number ) {
    if ( preg_match_all( $pattern, $subject, $matches, PREG_OFFSET_CAPTURE ) == false ) {
        return false;
    }
    return $matches[$number][count( $matches[0] ) - 1][1];
}

您可以在官方文档中获取有关preg_match_all here的详细信息。

以我的模式为例:

0 =&gt;所有文字
1 =&gt; href值
2 =&gt; innerHTML

echo '<pre>';
echo get_offset_last_match( $pattern, $subject, 0 ) . PHP_EOL; // all text
echo get_offset_last_match( $pattern, $subject, 1 ) . PHP_EOL; // href value
echo get_offset_last_match( $pattern, $subject, 2 ) . PHP_EOL; // innerHTML
echo '</pre>';
die();

输出是:

140
149
174

我的功能(文字):

function get_text_last_match( $pattern, $subject, $number ) {
    if ( preg_match_all( $pattern, $subject, $matches, PREG_OFFSET_CAPTURE ) == false ) {
        return false;
    }
    return $matches[$number][count( $matches[0] ) - 1][0];
}

示例代码:

echo '<textarea style="font-family: Consolas: font-size: 14px; height: 200px; tab-size: 4; width: 90%;">';
echo 'ALL   = ' . get_text_last_match( $pattern, $subject, 0 ) . PHP_EOL; // all text
echo 'HREF  = ' . get_text_last_match( $pattern, $subject, 1 ) . PHP_EOL; // href value
echo 'INNER = ' . get_text_last_match( $pattern, $subject, 2 ) . PHP_EOL; // innerHTML
echo '</textarea>';

输出是:

ALL   = <a href="https://duckduckgo.com/">DuckDuckGo</a>
HREF  = https://duckduckgo.com/
INNER = DuckDuckGo

答案 2 :(得分:1)

“贪婪”是这里的关键词。 *默认为贪婪*?将贪婪限制在最低限度。

因此解决方案是使用组合,例如(搜索最后一个期间,后跟空白):

/^.*\.\s(.*?)$/s
  • ^是文本的开头
  • 。*吃得尽可能多,包括匹配的模式
  • \。\ s是一个空格,后跟一个空格(我要查找的内容)
  • (。*?)尽量少吃东西。捕获组(),以便将其作为匹配组。
  • $个文本结尾
  • s-确保忽略换行符(不视为$和^,。点匹配换行符)

答案 3 :(得分:0)

preg_match不支持反向搜索,因为它不是必需的。

您可以创建一个RegExp,其中包含与任何内容匹配的贪婪(默认)前瞻(如(?<=.*)stuff)。这样你就可以获得最后一次比赛了。

此处官方文档中的详细信息:preg_match