代码之家  ›  专栏  ›  技术社区  ›  nbevans

使用平衡组的正则表达式

  •  1
  • nbevans  · 技术社区  · 14 年前

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

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

    我在解析正则表达式时遇到问题,因为它没有考虑嵌套的IF/ENDIF块。

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

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

    我一直在玩平衡小组游戏,到目前为止,我想出了以下几点:

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

    但这并不像我所期望的那样。例如,它捕获了许多空组。帮忙?

    1 回复  |  直到 14 年前
        1
  •  5
  •   Kobi    14 年前

    要使用平衡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+) 例如,对上次捕获的值进行分组。在我的正则表达式中,我保留了 Name Contents 简单正则表达式的组,并限制 目录 组-regex仍然包在 IF 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 . 如果你想抓住 我的女人 的内容,您必须在 目录 组(如果必须的话,可以使用lookahead来绕过它-将整个regex包装在 (?=...) ,但您需要使用位置和长度,以某种方式将其放入逻辑结构中)。

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