匹配正则表达式中的可选字符串

时间:2014-03-10 14:15:44

标签: php regex

我在正则表达式中匹配可选模式组时遇到问题。 元字符*和+是贪婪的,所以我认为元字符?也会贪婪,但它似乎没有像我想的那样发挥作用。

理论上我假设如果我们选择使模式组可选,如果在字符串中找到模式组,它将在匹配结果中返回,如果找不到我们仍将获得整体匹配结果,但结果中缺少此匹配。

实际发生的情况是,如果我的模式在字符串中匹配,它不包含在匹配结果中,正则表达式似乎注意到模式组是可选的,并且只是不打算甚至尝试匹配它。

如果我们设置测试并将此可选模式组更改为非可选模式,则regex会将其包含在匹配结果中,但这仅适用于测试,因为有时此模式在字符串中不可用。

我需要在结果中包含匹配的原因是因为我需要匹配结果以便在以后进行分析。

Encase我没有很好地描述这个场景,我在PHP中设置了一个非常简单的例子。

$string = 'This is a test, Stackoverflow. 2014 Cecili0n';

if(preg_match_all("~(This).*?(Stackoverflow)?~i",$string,$match))
    print_r($match);

结果

Array
(
    [0] => Array
        (
            [0] => This
        )

    [1] => Array
        (
            [0] => This
        )

    [2] => Array
        (
            [0] => 
        )
)

(Stackoverflow)?是可选模式,如果我们运行上面的代码,即使该模式在字符串中可用,也不会在匹配结果中返回。

如果我们强制使用此模式组,它将在结果中返回,如下所示。

if(preg_match_all("~(This).*?(Stackoverflow)~i",$string,$match))
    print_r($match);

结果

Array
(
    [0] => Array
        (
            [0] => This
        )

    [1] => Array
        (
            [0] => This
        )

    [2] => Array
        (
            [0] => Stackoverflow
        )
)

我怎样才能做到这一点?对我来说,获得有关如何找到匹配的准确数据非常重要。

感谢您对此事的任何想法。

2 个答案:

答案 0 :(得分:12)

这里发生了什么

这可能会令人惊讶,但实际上是预期的行为。让我们分解正则表达式并将其翻译成人类可读的术语:

(This)               Match "This" literally
.*?                  Match any character **as few times as possible**,
                     while still allowing the rest of the expression to match
(Stackoverflow)?     Match "Stackoverflow" literally **if possible**

所以会发生什么:

  • 正则表达式引擎匹配“This”。
  • 然后必须考虑*?量词应匹配的字符数。
  • 假设我们匹配零个字符。这是否允许表达式的其余部分匹配?换句话说,(Stackoverflow)?匹配“是否是测试,Stackoverflow。2014年Cecili0n”?
  • 子模式是可选的,所以它确实如此!因此,.*?匹配零个字符。
  • 最终子模式(Stackoverflow)?匹配什么?显然没有尝试匹配的位置。

最终结果:两个量化的子模式都匹配空字符串。

如何获得预期结果

如果使所有可选项不起作用,您如何选择匹配“Stackoverflow”?通过明确说明正则表达式引擎的可接受选项:

~(This)(.*?(Stackoverflow)|.*?)~i

这指示引擎尽可能多地匹配文字“Stackoverflow”,或者尽可能多地匹配。通过列出“Stackoverflow included”选项,您可以放心,如果文本中确实存在,则会匹配。

显然.*?选项在这个例子中并没有多大意义,但是我将其保留原样,因为我想描述一个无论实际正则表达式如何都能工作的“机械”转换。 / p>

请注意,为了保持与原始正则表达式的完全等效,为结构目的而引入的额外组必须是非捕获的:

~(This)(?:.*?(Stackoverflow)|.*)~i

<强> See it in action

答案 1 :(得分:0)

我已经尝试过这个,但似乎无法打破它。同时,一个可行的选择是进行两次测试,如下面的例子所示

$string = 'This is a test, Stackoverflow. 2014 Cecili0n';
$pattern1 = "~(This).*?(Stackoverflow)~i";
$pattern2 = "~(This).*?~i";

if(preg_match_all($pattern1,$string,$match)) {
    print_r($match);
} elseif(preg_match_all($pattern2,$string,$match)) {
    print_r($match);
}

当我找到更好的东西时,我会更新答案。