我正在尝试匹配一个可能嵌套的模式
下面是一些示例数据,我想从中提取{{ 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
,因此整个模式可以缩短一点。