匹配打开/关闭标签,接受另一个具有相同名称的打开/关闭标签(反击)

时间:2014-05-24 20:45:52

标签: php regex tags

正则表达的粉丝,又来了。 上一次,Casimir和Hippolyte为我的问题找到了一个优雅的解决方案。

Regex: matching open/close tags which accepts another open/close tag with same name

自从他(她的?)正则表达式开始以来,程序发生了一些变化, 我可以设法找到一个有效的解决方案。 但是,我对它并不完全满意。

问题是现在有两种类型的组件:

  • 那些打开标签以加号(+)
  • 结束的那些
  • 那些打开标签以减号( - )
  • 结束的那些

但是,它们都有相同的结束标记。 此外,两种类型的组件都可以包含其他类型(加号可以包含减号,反之亦然)。

我只需要获取“plus components”的内容。

<?php



$subject = '

{{poo+}}            # T1
    Hello

    {{poo-}}         # T2
        Nested 1
    {{/poo}}        # T3

{{/poo}}            # T4


{{poo+}}             # T5
    Bye
{{/poo}}            # T6



';



// The solution below works, but I'm forced to capture all types of components.
// I can differentiate them later using php...but I'm looking for a regex that does that immediately.
//
// The reason why is that in the real program, there are three components types, and the syntax is
// slightly more complex (so the regex would be slower to try all three types of components than just one),
// and there could be more component instances.

$p = '`(?x)
{{(\w+)([+-])}}
# ( # you need probably this capture group later
(?>
    [^{]++
  |
    { (?!{)
  |
    {{ (?! /? \1 \b) # if needed you can add }} in the lookahead
  |
    (?R)
)*
# )
{{/\1}}
`';


preg_replace_callback($p, function($match){
    var_dump($match);
}, $subject);

2 个答案:

答案 0 :(得分:1)

Bonjour ling,

这可能是本周最有趣的正则表达式问题之一。

我还没有研究过其他人的细节,所以从头开始这就是我的建议。

(?x)
(?>{{(?:[\w-]+(\+)?)}}
  (?:
    [^{}]++
    |
    ((?>{{[\w+-]+}}(?:[^{}]++|(?>(?2)))+{{/[\w+-]+}}))
  )++
{{/[\w+-]+}}
)
(?(1)|(*SKIP)(?!))

它是如何运作的?

密钥非常简单:我们将外部分隔符与{{(?:[\w-]+(\+)?)}}匹配,可选择将+中的poo+捕获到第1组(如果有)。这允许我们最后在(?(1)|(*SKIP)(?!))中检查我们在开始时是否有正确的分隔符(对第1组进行条件检查)。如果是,则在该阶段匹配成功。如果不是,我们跳过整个匹配,阻止引擎在嵌套集上尝试匹配。

其他详情:在分隔符之间,我们会多次匹配此表达式:

[^{}]++
|
((?>{{[\w+-]+}}(?:[^{}]+|(?2))+{{/[\w+-]+}}))
  1. 顶行[^{}]++允许我们匹配任何不是左右括号的内容。
  2. 如您所知,中间线是OR
  3. 整个底线被捕获到第2组,它通过(?2)子程序调用引用自身。此行是一个递归蓝图,用于匹配嵌套在外部表达式中的大括号集。
  4. 不相关的详细信息

    当你说his (her?)时,你的意思是h(?:is|er),对吗? :)

答案 1 :(得分:1)

你可以做些什么来确保你匹配poo +标签而不破坏递归调用的可能性,就是用一个测试是否达到递归级别的条件替换([+-])。例如:

$p = '`(?x)
{{(\w+) (?(R)[+-]|\+) }}
# ( # you need probably this capture group later
(?>
    [^{]++
  |
    { (?!{)
  |
    {{ (?! /? \1 \b) # if needed you can add }} in the lookahead
  |
    (?R)
)*
# )
{{/\1}}
`';

这是一个简单的IF..THEN..ELSE:

(?(R)     # IF    the recursion level has been reached
    [+-]  # THEN  matches any kind of tags
  | \+    # ELSE  matches only + tags
)