Skip to main content
 首页 » 编程设计

c#之使用平衡组的正则表达式

2024年02月03日30exmyth

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

foo bar 
%IF MY_VAR 
  some text 
  %IF OTHER_VAR 
    some other text 
  %ENDIF 
%ENDIF 
bar foo 

我用来解析它的正则表达式有问题,它没有考虑嵌套的 IF/ENDIF block 。

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

我一直在阅读有关平衡捕获组(.NET 正则表达式库的一项功能)的文章,因为我知道这是在 .NET 中支持“递归”正则表达式的推荐方法。

我一直在研究平衡组,到目前为止得出了以下结论:

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

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

请您参考如下方法:

要使用平衡的 IF 语句捕获整个 IF/ENDIF block ,您可以使用此正则表达式:

%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+)例如,最后捕获的值的组。在我的正则表达式中,我保留了 Name和 Contents将您的简单正则表达式分组,并限制 Contents 内的平衡group - 正则表达式仍然包含在 IF 中和 ENDIF .

当您的数据更复杂时,If 会变得有趣。例如:

%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 .如果你想捕获 MY_VAR 上的两个 ifs的内容,您必须在其 Contents 上重新运行正则表达式组(如果必须的话,你可以通过使用前瞻来绕过它 - 将整个正则表达式包装在 (?=...) 中,但是你需要使用位置和长度以某种方式将它放入逻辑结构中)。

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