重叠匹配preg_match_all,模式以重复字符结束

时间:2016-11-20 17:47:06

标签: php regex

我想做类似于问题preg_match_all how to get *all* combinations? Even overlapping ones的事情并找到给定模式的所有匹配,即使它们重叠(例如,匹配字符串ABABA和模式ABA应该返回2个匹配,而不仅仅是第一个匹配)。

但我有一个额外的约束:我的模式可以以重复说明符结束。我们以+为例:这意味着模式/A+/和主题"AA"应该返回3个匹配项:

  • 从索引0开始匹配"AA"
  • 从索引1开始匹配"A"
  • 从索引0开始匹配"A"

以下模式,根据上述问题建议的解决方案,未能匹配所有3个结果:

  • 模式/(?=(A+))/仅查找前2个匹配但不查找最后一个匹配
  • 模式/(?=(A+?))/仅查找最后2个匹配但不查找第一个匹配

我现在唯一的解决方法是保持贪婪的版本并尝试对每个匹配减去最后一个字符应用模式,重复此操作直到它不再匹配,例如:

$all_matches = array ();
$pattern = 'A+';

preg_match_all("/(?=($pattern))/", "AA", $matches, PREG_SET_ORDER);

foreach ($matches as $match) {
    do {
        $all_matches[] = $match[1];
        $subject = substr($match[1], 0, -1);
    }
    while (preg_match("/^($pattern)/", $subject, $match));
}

有没有更好的解决方案来实现这个使用preg_match_all或类似的?

1 个答案:

答案 0 :(得分:1)

您希望在一个索引处获得多个匹配,这在1个正则表达式匹配操作中是不可能的。你实际上需要

  • 查找字符串和
  • 中所有子字符串的组合
  • 只保留与您的模式完全匹配的那些。

请参阅PHP demo

function find_substrings($r, $s) {
  $res = array();
  $cur = "";
  $r = '~^' . $r . '$~';
  for ($q = 0; $q < strlen($s); ++$q) {
    for ($w = $q; $w <= strlen($s); ++$w) {
        $cur = substr($s, $q, $w-$q);
        if (preg_match($r, $cur)) {
            array_push($res, $cur);
        }
    }
  }
  return $res;
}
print_r(find_substrings("ABA", "ABABA"));
// => Array ( [0] => ABA [1] => ABA )
print_r(find_substrings("A+", "AA"));
// => Array ( [0] => A [1] => AA [2] => A )