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

问题描述 投票:8回答:4

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

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

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

php regex preg-match
4个回答
24
投票

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

  • 使用preg_match_allPREG_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
投票

我不明白你想要什么,因为它取决于将捕获多少组,我根据组号创建了捕获最后一次捕获的偏移的函数,在我的模式中,有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 =>所有文字 1 => href值 2 => 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

1
投票

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

所以解决方案是使用组合,例如(搜索最后一个句点后跟一个空格):

/^.*\.\s(.*?)$/s
  • ^是文本的开头
  • 。*尽可能多地吃,包括匹配模式
  • \。\ s是后面跟着一个空格的时期(我要找的是什么)
  • (。*?)尽量少吃。捕获组(),以便我可以将其作为匹配组来处理。
  • $结尾的文字
  • s - 确保忽略换行符(不处理为$和^,。dot匹配换行符)

0
投票

qazxsw poi不支持反向搜索,因为没有必要。

您可以创建一个RegExp,其中包含与任何内容匹配的贪婪(默认)前瞻(如preg_match)。这样你应该得到你的比赛的最后一次出现。

来自官方文档的详细信息:(?<=.*)stuff

© www.soinside.com 2019 - 2024. All rights reserved.