PHP 的 preg_match() 返回最后一个匹配项的位置


PHP's preg_match() returns the position of the last match

With

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

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

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

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

  • 使用带有PREG_SET_ORDER标志和end($matches)preg_match_all将为您提供最后一个匹配集
  • 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]);

演示

方式2:找到反转模式的反转字符串的第一个匹配项,并显示反转结果。

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

演示

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

方式 3从 x 跳到 x 并检查负前瞻是否从字符串末尾没有其他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]);

或带有"回火量词">

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

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

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

/^.*'.'s(.*?)$/s
  • ^是文本的开头
  • .*尽可能多地吃东西,包括匹配的模式
  • ''.'s句号后跟一个空格(我正在寻找什么(
  • (.*?)尽可能少吃。捕获组 ((,以便我可以将其作为匹配组进行寻址。
  • $文本末尾
  • s - 确保忽略换行符(
  • 不被视为$^ - .(点(匹配换行符(

我不明白你想要什么,因为这取决于将捕获多少组。

我做了一个函数,根据组号捕获上次捕获的偏移量。在我的模式中,我有三个组:第一组,完全捕获,另外两组,子组。

模式示例代码:

$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的详细信息。

以我的模式为例:

0 => 所有文本1 => href 值2 => 内部HTML

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

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

您可以创建一个正则表达式,其中包含与任何内容(如 (?<=.*)stuff(匹配的贪婪(默认(前瞻。这样,您应该获得匹配的最后一次出现。

官方文档中的详细信息preg_match