1

我有一个基本的文本模板引擎,它使用如下语法:

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)(?!))

但这并不完全符合我的预期。例如,它捕获了很多空组。帮助?

4

1 回答 1

5

要使用平衡的 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

这里的要点是:您不能在一个Match以上的每个命名组中捕获。例如,您只会获得一(?<Name>\w+)组最后捕获的值。在我的正则表达式中,我保留了简单正则表达式的NameandContents组,并限制了组内的平衡Contents- 正则表达式仍然包含在IFand中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。如果你想捕获两个 ifs onMY_VAR的内容,你必须在其Contents组上重新运行正则表达式(如果必须,你可以通过使用前瞻来绕过它 - 将整个正则表达式包装在 中(?=...),但你需要把它以某种方式使用位置和长度进入逻辑结构)。

现在,我不会解释太多,因为您似乎掌握了基础知识,但是关于内容组的简短说明 - 我使用所有格组来避免回溯。否则,点可能最终匹配整个IFs 并打破平衡。组上的惰性匹配行为类似(( )+?而不是(?> )+)。

于 2010-11-26T15:04:55.553 回答