【问题标题】:matching open/close tags which accepts another open/close tag with same name (strikes back)匹配打开/关闭标签,它接受另一个具有相同名称的打开/关闭标签(反击)
【发布时间】:2014-05-24 20:45:51
【问题描述】:

Regex 粉丝们,大家好。 上次,Casimir 和 Hippolyte 找到了一个优雅的解决方案来解决我的问题。

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

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

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

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

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

我只需要获取“加组件”的内容。

<?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);

【问题讨论】:

标签: php regex tags


【解决方案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
)

【讨论】:

  • 它似乎是这个案例的完美工具。谢谢。
【解决方案2】:

你好,

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

我还没有详细研究其他人做了什么,所以我建议从头开始。

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

它是如何工作的?

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

其他细节:在分隔符之间,我们可以多次匹配这个表达式:

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

不相关的细节

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

【讨论】:

  • 如果可以的话,我也会接受你的回答。在 10000 次迭代中,结果证明您的解决方案比 Casimir 和 Hippolyte 的解决方案稍快(0.18 对 0.185)。但是,我最终选择了 CaH 的解决方案,因为在 h(?:is|er) 解决方案中,组件的定义只需要定义一次。在您的解决方案中,我们必须在模式的入口以及递归级别定义它。另一方面,您的解决方案似乎更灵活,所以我真的不知道最终会实施哪一个。感谢您提供另一种方法(越多越好)。
  • 哦......嗯......如果我知道你关心这个,我会做一些明显的调整,这样代码就不会重复。这实际上就是我为自己的正则表达式所做的,但对于大多数人来说这太难读了。太糟糕了,但当然 Casimir 总是想出绝妙的解决方案,所以我不能在那里抱怨。
  • @ling 乍一看,我可以将其减少到 115 个可打印字符,而 C&H 为 103 个(删除他的 cmets),所以他显然在这一点上获胜。不过,如果将正则表达式代码的大小作为标准,我建议提前宣布——我可能也会缩短我的答案。
  • 这不是关于大小,而是更多关于干法。如果我知道这将成为一个标准,我也会在问题中这么说。您的 anwser 对我来说是一笔宝贵的财富,我想,对于任何进一步的读者来说也是如此。太遗憾了,我只能接受一个答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-21
  • 2010-11-26
  • 2011-01-27
  • 2016-03-17
  • 1970-01-01
相关资源
最近更新 更多