PHP RegEx以匹配嵌套模式(可能是递归)



我正在尝试匹配一个可能嵌套的模式
下面是一些示例数据,我想从中提取{{ loop ... }元素内部的内容:

<ul>
{{ loop #users as #u }}
<li>{{ #u.first_name }} {{ #u.last_name }}</li>
{{ endloop }}
</ul>

我用这个RegEx:做对了

/{{s+loops+#([a-zA-Z_][a-zA-Z0-9_]*)((?:.[a-zA-Z0-9_]+)*)s+ass+#([a-zA-Z_][a-zA-Z0-9_]*)s+}}(.*){{s+endloops+}}/sU

解释:

  • /
  • {{开始打开循环元素
    • s+loops+循环关键字
    • #([a-zA-Z_][a-zA-Z0-9_]*)变量名(例如:#var(
    • ((?:.[a-zA-Z0-9_]+)*)可选变量键(例如:#var.key(
    • s+ass+作为关键字
    • #([a-zA-Z_][a-zA-Z0-9_]*)s+别名变量名(例如:#alias(
  • 打开循环元素的}}结束
  • (.*)循环内容
  • {{s+endloops+}}关闭循环元素
  • /sU

哪里失败

对于嵌套循环,我需要获得第一级循环的内容(因为内容随后在我的项目中递归解析(。以下是一些示例数据:

1| <ul>
2|     {{ loop #users as #u }}
3|         <li>
4|             {{ #u.first_name }} {{ #u.last_name }}
5|             <ul>
6|                 {{ loop #u.friends as #f }}
7|                     <li>{{ #f.first_name }} {{ #f.last_name }}</li>
8|                 {{ endloop }}
9|             </ul>
10|         </li>
11|     {{ endloop }}
12| </ul>
13| 
14| {{ loop #foo as #bar }}
15|     <a href="#">{{ #bar }}</a>
16| {{ endloop }}

有了此内容,模式将在遇到的第一个{{ endloop }}处停止(行2-8(
如果我删除U标志(unreedy(,我就不能使用多个循环,因为即使它们是不同的循环(行2-16(,它也会停止到最后一个{{ endloop }}
我有一个使用/m标志(多行(的模式的早期版本,但它也失败了,因为它只匹配最深级别的循环(第6-8行(。

我尝试了很多次(大部分是在regexr.com上完成的(,但没有看到任何进展。我搜索了一个关于"递归模式">的解决方案,我发现最好的是这个问题,但经过多次尝试,我无法将其适应我的项目。


  • 是否有一个标志/标志组合可以为这种模式提供优先级
  • 我读过一些关于使用(?R)在RegEx中递归的文章,但还没有成功使用它,这对我的情况有帮助吗
  • 显而易见的最后一个问题:如何匹配第一级循环的全部内容

我不仅在寻找解决方案,我真的很想了解如何解决这个问题。链接到当前RegexR:RegexR.com/426fd

以下是对您的问题的性能修复(它需要几百步,而不是几千步(:

{{s+loops+#(w+)[^#]*#(w+)s*}}(?:[^{]*+|(?R)|{+)*{{s+endloops+}}

点击此处查看实时演示

RegExp细分:

  • {{s+loops+#(w+)[^#]*#(w+)s*}}匹配起始循环结构并捕获散列词
  • (?:非捕获组启动
    • [^{]*+所有格地匹配除{之外的任何内容
    • |
    • (?R)循环整个模式
    • |
    • {+匹配任意数量的大括号
  • )*尽可能匹配
  • {{s+endloops+}}匹配结束结构

以下是您当前模式的快速修复:

{{s+loops+#([a-zA-Z_]w*)((?:.w+)*)s+ass+#([a-zA-Z_]w*)s*}}((?:(?!{{s+(?:end)?loops).|(?R))*){{s+endloops+}}

请注意,不需要U修饰符就可以按预期运行此模式,但仍然需要.s修饰符来匹配任何字符。

查看regex演示

主要区别在于CCD_ 34被CCD_。它匹配0次或多次重复:

  • (?!{{s+(?:end)?loops).-未启动符合以下模式的序列的任何字符(.(:
    • {{-一个{{子串
    • s+-1+空白
    • (?:end)?-可选的end子字符串
    • loop-一个loop子串
    • s-空白
  • |
  • (?R)-整个正则表达式模式

此外,如果不使用u修饰符或(*UCP)PCRE动词,[a-zA-Z0-9_]等于w,因此整个模式可以缩短一点。

最新更新