在可变长度lookbehind中平衡组
TL; DR:在.NET的lookbehinds内使用捕获(尤其是平衡组)会改变获得的捕获,尽pipe它不应该有所作为。 什么是.NET的向后看,打破了预期的行为?
我试图想出这个问题的答案,作为与.NET平衡组织玩弄的借口。 但是,我不能让他们在一个可变长度lookbehind内工作。
首先,请注意,我不打算高效地使用这个特定的解决scheme。 这更多的是因为学术的原因,因为我觉得有一些事情发生在我不知道的可变长度lookbehind上。 而且我知道,将来我可以派上用场的时候,实际上我需要使用类似的东西来解决问题。
考虑这个input:
~(ab (c) d (ef (g) h) i) j (k (l (m) n) p) q
我们的目标是匹配所有的字母,这些字母在~
之前的圆括号内,不pipe多深(从a
到i
)。 我试图在向后看的地方检查正确的位置,这样我就可以在一个调用Matches
得到所有的字母。 这是我的模式:
(?<=~[(](?:[^()]*|(?<Depth>[(])|(?<-Depth>[)]))*)[az]
在后面我试图find一个~(
然后我使用命名的组堆栈Depth
来计算无关的开括号。只要在~(
)中打开的括号永远不会closures,则后向应该匹配。 (?<-Depth>...)
不能从堆栈中popup任何东西,并且向后看会失败(也就是说,对于来自j
所有字母)。不幸的是,这不起作用,而是匹配a
, b
, c
, e
, f
, g
和m
,所以只有这些:
~(ab (c) _ (ef (g) _) _) _ (_ (_ (m) _) _) _
这似乎意味着,如果我已经closures了一个括号,那么lookbehind就不能匹配任何东西, 除非我回到之前的最高嵌套级别。
好吧,这可能意味着我的正则expression式有些奇怪,或者我没有正确理解平衡组。 但是后来我尝试了这个,不看后面。 我为每个字母创build了一个string,如下所示:
~(zb (c) d (ef (x) y) g) h (i (j (k) l) m) n ~(az (c) d (ef (x) y) g) h (i (j (k) l) m) n ~(ab (z) d (ef (x) y) g) h (i (j (k) l) m) n .... ~(ab (c) d (ef (x) y) g) h (i (j (k) l) z) n ~(ab (c) d (ef (x) y) g) h (i (j (k) l) m) z
并在这些模式中使用这种模式:
~[(](?:[^()]*|(?<Depth>[(])|(?<-Depth>[)]))*z
根据需要,所有情况都是匹配的,其中z
代替了a
和i
之间a
一个字母,以及之后的所有情况。
那么,这个(可变长度)向后看是什么打破了平衡组的使用? 我试图整个晚上研究这个(并find这样的页面),但我无法find一个单一的使用这个在向后看。
我也很高兴,如果有人可以链接到我的一些深入的信息,如何正则expression式引擎处理.NET特定的内部function。 我发现了这篇令人惊叹的文章 ,但似乎并没有涉及到(可变长度)的lookbehinds,例如。
我想我明白了。
首先,正如我在其中一个评论中提到的那样, (?<=(?<A>.)(?<-A>.))
永远不会匹配。
但后来我想,怎么样(?<=(?<-A>.)(?<A>.))
? 它确实匹配!
(?<=(?<A>.)(?<A>.))
? 与"12"
匹配, A
捕获"1"
,如果我们看“ Captures
集合,它是{"2", "1"}
– 前两个,然后是 – 颠倒过来。
因此,在向后看的时候,.net会从右到左匹配和捕获 。
现在,我们如何才能从左到右捕捉? 这很简单,真的 – 我们可以用引擎来欺骗引擎:
(?<=(?=(?<A>.)(?<A>.))..)
应用到你的原始模式,我想到的最简单的select是:
(?<= ~[(] (?= (?: [^()] | (?<Depth>[(]) | (?<-Depth>[)]) )* (?<=(\k<Prefix>)) # Make sure we matched until the current position ) (?<Prefix>.*) # This is captured BEFORE getting to the lookahead ) [az]
这里面的挑战是,现在平衡的部分可能在任何地方结束,所以我们使它到达目前的位置(像\G
或\Z
这样的东西在这里是有用的,但我不认为.net有)
这种行为很有可能在某处被logging下来,我会试着去查找它。
这是另一种方法。 这个想法很简单 – .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
我认为问题是数据而不是模式。 数据有'Post'项目需要匹配,比如
(ab(c)def)
de和f需要匹配。 更平衡的数据将是
(ab(c)(d)(e)(f))
所以,我对这个示例数据的处理需要花括号后的匹配情况:
〜(ab(c)d(ef(g)h)i)jk
在那里j&k应该被忽略…我的模式失败,并抓住他们。
有意思的是,我给捕获组找出了他们进来的地方,j和k进来捕获三个。 我留给你的不是答案,而是试图看看你能不能改进。
(~ # Anchor to a Tilde ( # Note that \x28 is ( and \x29 is ) ( # --- PRE --- (?<Paren>\x28)+ # Push on a match into Paren ((?<Char1>[^\x28\x29])(?:\s?))* )+ # Represents Sub Group 1 ( #---- Closing ((?<Char2>[^\x28\x29])(?:\s?))* (?<-Paren>\x29)+ # Pop off a match from Paren )+ ( ((?<Char3>[^\x28\x29])(?:\s?))* # Post match possibilities )+ )+ (?(Paren)(?!)) # Stop after there are not parenthesis )
这是我用我自己创build的一个工具(也许有一天我会发布)打破的比赛。 请注意,˽显示空间匹配的位置。
Match #0 [0]: ~(a˽b˽(c)˽d˽(e˽f˽(g)˽h)˽i)˽j˽k ["1"] → [1]: ~(a˽b˽(c)˽d˽(e˽f˽(g)˽h)˽i)˽j˽k →1 Captures: ~(a˽b˽(c)˽d˽(e˽f˽(g)˽h)˽i)˽j˽k ["2"] → [2]: (e˽f˽(g)˽h)˽i)˽j˽k →2 Captures: (a˽b˽(c)˽d˽, (e˽f˽(g)˽h)˽i)˽j˽k ["3"] → [3]: (g →3 Captures: (a˽b˽, (c, (e˽f˽, (g ["4"] → [4]: g →4 Captures: a˽, b˽, c, e˽, f˽, g ["5"] → [5]: ˽i) →5 Captures: ), ), ˽h), ˽i) ["6"] → [6]: i →6 Captures: ˽, h, ˽, i ["7"] → [7]: →7 Captures: ˽d˽, , ˽j˽k, ["8"] → [8]: k →8 Captures: ˽, d˽, ˽, j˽, k ["Paren"] → [9]: ["Char1"] → [10]: g →10 Captures: a, b, c, e, f, g ["Char2"] → [11]: i →11 Captures: ˽, h, ˽, i ["Char3"] → [12]: k →12 Captures: ˽, d, ˽, j, k