首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >与嵌套模式匹配的RegEx (可能的递归)

与嵌套模式匹配的RegEx (可能的递归)
EN

Stack Overflow用户
提问于 2018-10-31 08:32:54
回答 2查看 245关注 0票数 0

我正在尝试匹配一个可能嵌套的模式。

下面是一些示例数据,我想在其中提取{{ loop ... }元素中的内容:

代码语言:javascript
复制
<ul>
    {{ loop #users as #u }}
        <li>{{ #u.first_name }} {{ #u.last_name }}</li>
    {{ endloop }}
</ul>

我用这个RegEx正确地理解了它:

代码语言:javascript
复制
/{{\s+loop\s+#([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z0-9_]+)*)\s+as\s+#([a-zA-Z_][a-zA-Z0-9_]*)\s+}}(.*){{\s+endloop\s+}}/sU

解释:

  • /
  • 开环元素{{启动
    • \s+loop\s+ 循环关键字
    • #([a-zA-Z_][a-zA-Z0-9_]*)变量名(例如:#var)
    • ((?:\.[a-zA-Z0-9_]+)*)可选变量键(例如:#var__.key)
    • \s+as\s+ as关键字
    • #([a-zA-Z_][a-zA-Z0-9_]*)\s+别名变量名(ex:#alias)

  • 开环元件的}}
  • (.*)循环内容
  • {{\s+endloop\s+}}闭环元件
  • /sU

失败的地方

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

代码语言:javascript
复制
 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标志(不贪婪),就不能使用多个循环,因为即使它们是不同的循环(2-16行),它也会停止到最后一个循环。

我有一个使用/m标志(multiline)的以前版本的模式,但是它也失败了,因为它只匹配最深级别的循环(行6-8)。

我做了很多次尝试(大部分是在regexr.com上做的),但没有看到任何进展。我寻找一个关于“递归模式”的解决方案,我找到的最好的解决方案是这个问题,但经过多次尝试,我无法使它适应我的项目。

  • 是否有标志/标志组合来优先处理这种模式?
  • 我读过一些关于在RegEx中使用(?R)的递归,但是没有成功地使用它,这对我的情况有帮助吗?
  • 最明显的最后一个问题是:如何才能匹配第一级循环的全部内容?

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-10-31 09:08:57

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

代码语言:javascript
复制
{{\s+loop\s+#([a-zA-Z_]\w*)((?:\.\w+)*)\s+as\s+#([a-zA-Z_]\w*)\s*}}((?:(?!{{\s+(?:end)?loop\s).|(?R))*){{\s+endloop\s+}}

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

regex演示

主要的区别在于用.*代替(?:(?!{{\s+(?:end)?loop\s).|(?R))*。它匹配0次或多次重复:

  • (?!{{\s+(?:end)?loop\s). -没有启动符合以下模式的序列的任何字符(.):
    • {{ -一个{{子字符串
    • \s+ - 1+白空间
    • (?:end)? -一个可选的end子字符串
    • loop -一个loop子字符串
    • \s -一个空白

  • | -或
  • (?R) -整个正则表达式

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

票数 1
EN

Stack Overflow用户

发布于 2018-10-31 09:20:15

下面是针对您的问题的性能修正(它需要几百个步骤而不是邪恶的上千个回溯步骤):

代码语言:javascript
复制
{{\s+loop\s+#(\w+)[^#]*#(\w+)\s*}}(?:[^{]*+|(?R)|{+)*{{\s+endloop\s+}}

请参阅现场演示

RegExp细分:

  • {{\s+loop\s+#(\w+)[^#]*#(\w+)\s*}}匹配起始循环结构并捕获散列单词
  • (?:非捕获组的启动
    • [^{]*+除了拥有一个{之外,任何东西都匹配
    • |
    • (?R)递归整个模式
    • |
    • {+匹配任意数量的开口大括号

  • 尽可能多地匹配)*
  • {{\s+endloop\s+}}匹配结束结构
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53079213

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档