我有一个基本的文本模板引擎,它使用如下语法:
foo bar
%IF MY_VAR
some text
%IF OTHER_VAR
some other text
%ENDIF
%ENDIF
bar foo
我对用于解析它的正则表达式有一个问题,它没有考虑嵌套的 IF/ENDIF 块。
我当前使用的正则表达式是:%IF (?<Name>[\w_]+)(?<Contents>.*?)%ENDIF
我一直在阅读有关平衡捕获组(.NET 正则表达式库的一项功能)的内容,因为我知道这是在 .NET 中支持“递归”正则表达式的推荐方法。
我一直在研究平衡组,到目前为止已经得出以下结论:
(
(
(?'Open'%IF\s(?<Name>[\w_]+))
(?<Contents>.*?)
)+
(
(?'Close-Open'%ENDIF)(?<Remainder>.*?)
)+
)*
(?(Open)(?!))
但这并不完全符合我的预期。例如,它捕获了很多空组。帮助?
要使用平衡 IF 语句捕获整个 IF/ENDIF 块,可以使用以下正则表达式:
%IF\s+(?<Name>\w+)
(?<Contents>
(?> #Possessive group, so . will not match IF/ENDIF
\s|
(?<IF>%IF)| #for IF, push
(?<-IF>%ENDIF)| #for ENDIF, pop
. # or, anything else, but don't allow
)+
(?(IF)(?!)) #fail on extra open IFs
) #/Contents
%ENDIF
这里的重点是:你cannot捕获在单个Match
每个命名组中都不止一个。你只会得到一个(?<Name>\w+)
例如,最后捕获的值的组。在我的正则表达式中,我保留了Name
and Contents
简单的正则表达式组,并限制了内部的平衡Contents
组 - 正则表达式仍然包含在IF
and ENDIF
.
当您的数据更加复杂时,它会变得有趣。例如:
%IF MY_VAR
some text
%IF OTHER_VAR
some other text
%ENDIF
%IF OTHER_VAR2
some other text 2
%ENDIF
%ENDIF
%IF OTHER_VAR3
some other text 3
%ENDIF
在这里,您将获得两场比赛,一场是MY_VAR
,还有一个用于OTHER_VAR3
。如果你想捕获两个 ifMY_VAR
的内容,您必须在其上重新运行正则表达式Contents
组(如果必须的话,您可以通过使用前瞻来绕过它 - 将整个正则表达式包装在(?=...)
,但您需要使用位置和长度以某种方式将其放入逻辑结构中)。
现在,我不会解释太多,因为看起来你已经了解了基础知识,但关于内容组的简短说明 - 我使用了所有格组来避免回溯。否则,该点最终可能会匹配整个IF
并打破平衡。组中的惰性匹配的行为类似(( )+?
代替(?> )+
).
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)