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

C# 使用平衡组的正则表达式,c#,.net,regex,balancing-groups,C#,.net,Regex,Balancing Groups,我有一个基本的文本模板引擎,它使用如下语法: foo bar %IF MY_VAR some text %IF OTHER_VAR some other text %ENDIF %ENDIF bar foo 我对用于解析它的正则表达式有一个问题,因为它没有考虑嵌套的IF/ENDIF块 我正在使用的当前正则表达式是:%IF(?[\w\u]+)(?*?)%ENDIF 我一直在阅读关于平衡捕获组(一个.NET正则表达式库的功能)的文章,因为我知道这是支持.NET中“递归”正则表达

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

foo bar
%IF MY_VAR
  some text
  %IF OTHER_VAR
    some other text
  %ENDIF
%ENDIF
bar foo
我对用于解析它的正则表达式有一个问题,因为它没有考虑嵌套的IF/ENDIF块

我正在使用的当前正则表达式是:
%IF(?[\w\u]+)(?*?)%ENDIF

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

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

(
 (
  (?'Open'%IF\s(?<Name>[\w_]+))
  (?<Contents>.*?)
 )+
 (
  (?'Close-Open'%ENDIF)(?<Remainder>.*?)
 )+
)*
(?(Open)(?!))
(
(
(?“打开“%IF\s(?[\w\u]))
(?.*?)
)+
(
(?'关闭-打开“%ENDIF”)(?*?)
)+
)*
(?(开放)(?!))

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

要捕获带有平衡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
在这里,您将得到两个匹配项,一个用于
MY\u-VAR
,另一个用于
OTHER\u-VAR3
。如果您想在
MY_VAR
的内容中捕获这两个If,您必须在其
内容
组中重新运行正则表达式(如果必须将整个正则表达式包装在
(?=…)
中,您可以使用前瞻来绕过它,但您需要使用位置和长度以某种方式将其放入逻辑结构中)


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

要捕获带有平衡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
在这里,您将得到两个匹配项,一个用于
MY\u-VAR
,另一个用于
OTHER\u-VAR3
。如果您想在
MY_VAR
的内容中捕获这两个If,您必须在其
内容
组中重新运行正则表达式(如果必须将整个正则表达式包装在
(?=…)
中,您可以使用前瞻来绕过它,但您需要使用位置和长度以某种方式将其放入逻辑结构中)


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

所有这些,考虑使用解析器,它应该很容易地处理它。非常感谢你。我在我的程序中添加了一些递归,这样它就可以遍历嵌套的if/endif块。。。考虑这个输入文本:<代码> %,如果MyAvar某个文本%,如果另一个VaR %Endif。请注意,内部OTHER_VAR不是用ENDIF关闭的。但是,正则表达式与内部块匹配,而不是与外部块匹配。在这个特定场景中(即模板格式错误),如何使正则表达式与最外层的块匹配,而不是直接跳到最内层的块?@NathanE-问得好!我本来打算添加一段关于无效输入的内容,但尽管它太长了。。。我不确定有没有简单的方法来解决这个问题——平衡组的存在是为了避免在组不匹配的情况下进行匹配,在这种情况下很难为“正确”行为添加规则(就此而言,这个正则表达式捕获了一个平衡的if,即“正确”)。您可能会使用原始正则表达式。如果你想定义清晰的规则,你可能需要编写一个语法和解析器——它允许微调,并且可以提供更好的解析错误消息。没问题。这仍然是一个伟大的regex你想出了Kobi。如果将来需要为格式错误的模板提供更优雅的错误处理条件,或者可能需要向模板语言添加更多类型的标记,那么我很可能会重新设计它,使其成为一个合适的解析引擎。我想regex在那天之前都会很好的!除此之外,考虑使用解析器,它应该很容易地处理它。非常感谢你。我在我的程序中添加了一些递归,这样它就可以遍历嵌套的if/endif块。。。考虑这个输入文本:<代码> %,如果MyAvar某个文本%,如果另一个VaR %Endif。请注意,内部OTHER_VAR不是用ENDIF关闭的。但是,正则表达式与内部块匹配,而不是与外部块匹配。在这个特定场景中(即模板格式错误),如何使正则表达式与最外层的块匹配,而不是直接跳到最内层的块?@NathanE-问得好!我本来打算添加一段关于无效输入的内容,但尽管它太长了。。。我不确定有没有简单的方法来解决这个问题——平衡组的存在是为了避免在组不匹配的情况下进行匹配,在这种情况下很难为“正确”行为添加规则(就此而言,这个正则表达式捕获了一个平衡的if,即“正确”)。您可能会使用原始正则表达式。如果你想定义清晰的规则,你可能需要编写一个语法和解析器——它允许微调,并且可以提供更好的解析错误消息。没问题。这仍然是一个伟大的regex你想出了Kobi。如果将来需要为格式错误的模板提供更优雅的错误处理条件,或者可能需要向模板语言添加更多类型的标记,那么我很可能会重新设计它,使其成为一个合适的解析引擎。我想regex在那天之前都会很好的!