在可变长度的后视中平衡组

Mar*_*der 14 .net regex lookaround balancing-groups

TL; DR:在.NET的lookbehinds中使用捕获(特别是平衡组)会改变获得的捕获,尽管它不应该有所作为.什么是.NET的外观打破了预期的行为?

我试图找到另一个问题的答案,作为借助.NET平衡组的借口.但是,我不能让他们在可变长度的lookbehind内工作.

首先,请注意我并不打算高效地使用这个特定的解决方案.这更多是出于学术原因,因为我觉得可变长度的lookbehind有一些我不知道的事情.并且知道这可能在将来派上用场,当我真的需要使用这样的东西来解决问题时.

考虑这个输入:

~(a b (c) d (e f (g) h) i) j (k (l (m) n) p) q
Run Code Online (Sandbox Code Playgroud)

我们的目标是匹配所有的字母,这是由前括号内~,没有多么深跌(所以一切从ai).我的尝试是检查后视镜中的正确位置,以便我可以通过一次调用获得所有字母Matches.这是我的模式:

(?<=~[(](?:[^()]*|(?<Depth>[(])|(?<-Depth>[)]))*)[a-z]
Run Code Online (Sandbox Code Playgroud)

在lookbehind我尝试找到一个~(,然后我使用命名组堆栈Depth来计算无关的开括号.只要打开的括号~(永远不会关闭,那么lookbehind应该匹配.如果到达(?<-Depth>...)那个右括号,则无法从堆栈中弹出任何内容,并且lookbehind应该失败(即,对于所有来自的字母j).不幸的是,这不起作用.相反,我匹配a,b,c,e,f,gm.所以只有这些:

~(a b (c) _ (e f (g) _) _) _ (_ (_ (m) _) _) _
Run Code Online (Sandbox Code Playgroud)

这似乎意味着,一旦我关闭了一个单一的括号,看起来就无法匹配任何东西,除非我回到我以前最高的嵌套水平.

好吧,这可能只是意味着我的正则表达式有些奇怪,或者我没有正确理解平衡组.但后来我尝试了这个没有外观.我为每个字母创建了一个字符串,如下所示:

~(z b (c) d (e f (x) y) g) h (i (j (k) l) m) n
~(a z (c) d (e f (x) y) g) h (i (j (k) l) m) n
~(a b (z) d (e f (x) y) g) h (i (j (k) l) m) n
....
~(a b (c) d (e f (x) y) g) h (i (j (k) l) z) n
~(a b (c) d (e f (x) y) g) h (i (j (k) l) m) z
Run Code Online (Sandbox Code Playgroud)

并在每个上使用这种模式:

~[(](?:[^()]*|(?<Depth>[(])|(?<-Depth>[)]))*z
Run Code Online (Sandbox Code Playgroud)

并且根据需要,所有情况都匹配,其中z替换之间的字母ai在该失败之后的所有情况.

那么(可变长度)外观是什么打破了平衡组的使用呢?我整个晚上都试图研究这个(并且找到了像这样的页面),但我无法在后面找到一个这样的用途.

我也很高兴,如果有人可以链接我一些关于.NET正则表达式引擎如何在内部处理.NET特定功能的深入信息.我发现了这篇惊人的文章,但它似乎没有进入(可变长度)的lookbehinds,例如.

Kob*_*obi 13

我想我明白了.
首先,正如我在其中一条评论中提到的,(?<=(?<A>.)(?<-A>.))从不匹配.
但后来我想,怎么样(?<=(?<-A>.)(?<A>.))?它确实匹配!
怎么样(?<=(?<A>.)(?<A>.))?匹配"12",A捕获"1",如果我们看看Captures集合,它是{"2", "1"}- 前两个,然后一个 - 它是相反的.
因此,在一个lookbehind内,.net匹配并从右到左捕获.

现在,我们怎样才能让它从左到右捕捉?这很简单,真的 - 我们可以使用前瞻技巧来欺骗引擎:

(?<=(?=(?<A>.)(?<A>.))..)
Run Code Online (Sandbox Code Playgroud)

适用于您原来的图案,我提出的最简单的选择是:

(?<=
    ~[(]
    (?=
        (?:
            [^()]
            |
            (?<Depth>[(])
            |
            (?<-Depth>[)])
        )*
        (?<=(\k<Prefix>))   # Make sure we matched until the current position
    )
    (?<Prefix>.*)           # This is captured BEFORE getting to the lookahead
)
[a-z]
Run Code Online (Sandbox Code Playgroud)

这里的挑战是,现在平衡部分可能会在任何地方结束,所以我们让它一直到达当前位置(这里有类似\G\Z有用的东西,但我不认为.net有这个)

很可能这种行为记录在某处,我会尝试查找它.

这是另一种方法.这个想法很简单 - .net想要从右到左匹配?精细!拿出来:(
提示:从底部开始阅读 - 这就是.net的用法)

(?<=
    (?(Depth)(?!))  # 4. Finally, make sure there are no extra closed parentheses.
    ~\(
    (?>                     # (non backtracking)
        [^()]               # 3. Allow any other character
        |
        \( (?<-Depth>)?     # 2. When seeing an open paren, decreace depth.
                            #    Also allow excess parentheses: '~((((((a' is OK.
        |
        (?<Depth>  \) )     # 1. When seeing a closed paren, add to depth.
    )*
)
\w                          # Match your letter
Run Code Online (Sandbox Code Playgroud)

  • 刚刚找到[this](https://mail.mozilla.org/pipermail/es-discuss/2012-March/021378.html).包含(除其他外)"从右到左模式,为.NET的外观提供动力......".[本书](http://books.google.co.uk/books?id=6k7IfACN_P8C&pg=PA86&lpg=PA86&dq=.net+lookbehind+right-to-left&source=bl&ots=CoJb4Jnl2N&sig=4h38arO1NgCvCdTy3fx4zDa4yx8&hl=de&sa=X&ei=NNKmUOaCFYKd0QW05YHoCA&ved = 0CEYQ6AEwBA #v = onepage&q = .net%20lookbehind%20right-to-left&f = false)也确认了它. (3认同)
  • 自然,这在Mono上失败:http://ideone.com/GuCNVM。我的大多数模式在Mono上均失败。 (2认同)