使用平衡组的正则表达式

nbe*_*ans 1 .net c# regex balancing-groups

我有一个基本的文本模板引擎,使用这样的语法:

foo bar
%IF MY_VAR
  some text
  %IF OTHER_VAR
    some other text
  %ENDIF
%ENDIF
bar foo
Run Code Online (Sandbox Code Playgroud)

我有一个正则表达式的问题,我用来解析它,因为它没有考虑嵌套的IF/ENDIF块.

我正在使用的当前正则表达式是: %IF (?<Name>[\w_]+)(?<Contents>.*?)%ENDIF

我一直在阅读平衡捕获组(.NET的正则表达式库的一个特性),因为我理解这是支持.NET中"递归"正则表达式的推荐方法.

我一直在玩平衡组,到目前为止已经提出了以下建议:

(
 (
  (?'Open'%IF\s(?<Name>[\w_]+))
  (?<Contents>.*?)
 )+
 (
  (?'Close-Open'%ENDIF)(?<Remainder>.*?)
 )+
)*
(?(Open)(?!))
Run Code Online (Sandbox Code Playgroud)

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

Kob*_*obi 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
Run Code Online (Sandbox Code Playgroud)

这里的要点是:你不能Match每个命名组中的一个以上捕获.(?<Name>\w+)例如,您将只获得一组最后捕获的值.在我正则表达式,我不停的NameContents你简单的正则表达式的群体,并限制了内部的平衡Contents组-正则表达式仍包裹在IFENDIF.

如果您的数据更复杂,那么会变得有趣.例如:

%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                 
Run Code Online (Sandbox Code Playgroud)

在这里,你会得到两场比赛,一个MY_VAR,一个用于OTHER_VAR3.如果你想捕获MY_VAR内容上的两个ifs ,你必须重新运行它的Contents组上的正则表达式(你可以通过使用前瞻来解决它,如果你必须 - 包装整个正则表达式(?=...),但你需要把它以某种方式使用位置和长度进入逻辑结构).

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