使用平衡组的正则表达式

发布于 2024-10-05 07:01:12 字数 604 浏览 7 评论 0原文

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

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

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

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

我一直在阅读有关平衡捕获的 内容组(.NET 正则表达式库的一项功能)据我了解,这是在.NET 中支持“递归”正则表达式的推荐方法。

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

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

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

I have a basic text template engine that uses a syntax like this:

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

I have an issue with the regular expression that I am using to parse it whereby it is not taking into account the nested IF/ENDIF blocks.

The current regex I'm using is: %IF (?<Name>[\w_]+)(?<Contents>.*?)%ENDIF

I have been reading up on balancing capture groups (a feature of .NET's regex library) as I understand this is the recommended way of supporting "recursive" regex's in .NET.

I've been playing with balancing groups and have so far came up with the following:

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

But this is not behaving entirely how I would expect. It is for instance capturing a lot of empty groups. Help?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

不顾 2024-10-12 07:01:12

要使用平衡的 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 中捕获多个每个指定的组。例如,您只能获得最后捕获值的一个 (?\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                 

在这里,您将获得两个匹配项,一个用于 MY_VAR,另一个用于 OTHER_VAR3。如果您想捕获 MY_VAR 内容上的两个 if,则必须在其 Contents 组上重新运行正则表达式(如果您必须 - 将整个正则表达式包装在 (?=...) 中,但您需要使用位置和长度以某种方式将其放入逻辑结构中。

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

To capture a whole IF/ENDIF block with balanced IF statements, you can use this regex:

%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

The point here is this: you cannot capture in a single Match more than one of every named group. You will only get one (?<Name>\w+) group, for example, of the last captured value. In my regex, I kept the Name and Contents groups of your simple regex, and limited the balancing inside the Contents group - the regex is still wrapped in IF and ENDIF.

If becomes interesting when your data is more complex. For example:

%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                 

Here, you will get two matches, one for MY_VAR, and one for OTHER_VAR3. If you want to capture the two ifs on MY_VAR's content, you have to rerun the regex on its Contents group (you can get around it by using a lookahead if you must - wrap the whole regex in (?=...), but you'll need to put it into a logical structure somehow, using positions and lengths).

Now, I won't explain too much, because it seems you get the basics, but a short note about the contents group - I've uses a possessive group to avoid backtracking. Otherwise, it would be possible for the dot to eventually match whole IFs and break the balance. A lazy match on the group would behave similarly (( )+? instead of (?> )+).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文