TL;DR: 在.NET 的lookbehinds 中使用捕获(特别是平衡组)会更改获得的捕获,尽管它不会产生任何影响。 .NET 的lookbehind 是什么破坏了预期的行为?
我试图找到“另一个问题”的答案,作为尝试 .NET 平衡组的借口。但是,我无法让它们在可变长度的lookbehind中工作。 首先,请注意,我并不打算有效地使用这个特定的解决方案。这更多是出于学术原因,因为我觉得可变长度lookbehind发生了一些我不知道的事情。并且知道当我实际上需要使用这样的东西来解决问题时,这在将来会派上用场。
考虑这个输入:
~(a b (c) d (e f (g) h) i) j (k (l (m) n) p) q
目标是匹配括号内以
~
开头的所有字母,无论有多深(因此从
a
到 i
的所有字母)。我的尝试是检查后向查找中的正确位置,以便我可以在一次调用 Matches
中获取所有字母。这是我的模式:(?<=~[(](?:[^()]*|(?<Depth>[(])|(?<-Depth>[)]))*)[a-z]
在lookbehind中,我尝试找到一个
~(
,然后使用命名的组堆栈
Depth
来计算无关的左括号。只要 ~(
中打开的括号永远不会关闭,后向查找就应该匹配。如果到达右括号,(?<-Depth>...)
无法从堆栈中弹出任何内容,并且后向查找应该失败(即,对于 j
中的所有字母)。不幸的是,这不起作用。相反,我匹配 a
、b
、c
、e
、f
、g
和 m
。所以只有这些:~(a b (c) _ (e f (g) _) _) _ (_ (_ (m) _) _) _
这似乎意味着一旦我关闭了一个括号,后向查找就无法匹配任何内容,除非
我回到之前去过的最高嵌套级别。 好吧,这可能只是意味着我的正则表达式有些奇怪,或者我没有正确理解平衡组。但后来我尝试了这个,没有向后看。我为每个字母创建了一个字符串,如下所示:
~(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
并在每一个上都使用了这种模式:
~[(](?:[^()]*|(?<Depth>[(])|(?<-Depth>[)]))*z
并且根据需要,所有情况都匹配,其中
z
替换
a
和 i
之间的字母,之后的所有情况都会失败。那么(可变长度)lookbehind 会做什么来打破平衡组的这种使用呢?我整个晚上都试图研究这个(并找到像this one
这样的页面),但我在后向查找中找不到这个的单一用途。 如果有人可以将我链接到一些有关 .NET 正则表达式引擎如何在内部处理 .NET 特定功能的深入信息,我也会很高兴。我发现了
这篇令人惊叹的文章(?<=(?<A>.)(?<-A>.))
永远不会匹配。
但转念一想,那
(?<=(?<-A>.)(?<A>.))
(?<=(?<A>.)(?<A>.))
"12"
匹配,A
捕获 "1"
,如果我们查看 Captures
集合,它是 {"2", "1"}
- 前两个,然后一个 - 它是相反的。因此,在lookbehind内部时,.net从右到左匹配和捕获(?<=(?=(?<A>.)(?<A>.))..)
应用于您的原始模式,我想出的最简单的选项是:
(?<=
~[(]
(?=
(?:
[^()]
|
(?<Depth>[(])
|
(?<-Depth>[)])
)*
(?<=(\k<Prefix>)) # Make sure we matched until the current position
)
(?<Prefix>.*) # This is captured BEFORE getting to the lookahead
)
[a-z]
这里的挑战是,现在平衡部分可能在任何地方结束,所以我们让它一直到达当前位置(像
\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
(a b ( c ) d e f )
其中d e 和f 需要匹配。更
平衡的数据将会是 (a b (c)(d)(e)(f))
因此,我在此示例数据上采取的策略需要大括号后的赛后情况:
~(a b (c) d (e f (g) h) i) j k
其中 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
)
这是用我自己创建的工具(也许有一天我会发布)进行的比赛。请注意,˽ 显示匹配空格的位置。
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