14

TL;DR:在 .NET 的lookbehinds 中使用捕获(特别是平衡组)会改变获得的捕获,尽管它不应该产生影响。.NET 的lookbehinds 是什么破坏了预期的行为?

我试图想出对另一个问题的答案,以此作为玩弄 .NET 平衡组的借口。但是,我无法让它们在可变长度的后视中工作。

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

考虑这个输入:

~(a b (c) d (e f (g) h) i) j (k (l (m) n) p) q

目标是匹配所有字母,这些字母在括号内,前面有~,不管有多深(所以从ai)。我的尝试是在后视中检查正确的位置,以便我可以在一次调用中获取所有字母Matches。这是我的模式:

(?<=~[(](?:[^()]*|(?<Depth>[(])|(?<-Depth>[)]))*)[a-z]

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

~(a b (c) _ (e f (g) _) _) _ (_ (_ (m) _) _) _

这似乎意味着一旦我关闭了一个括号,lookbehind 就无法匹配任何东西,除非我回到我之前去过的最高嵌套级别。

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

~(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之间的一个字母失败。ai

那么(可变长度)lookbehind 做了什么打破了平衡组的这种使用?我整个晚上都试图研究这个(并找到了像这样的页面),但我在回顾中找不到这个的单一用途。

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

4

2 回答 2

13

我想我明白了。
首先,正如我在其中一个评论中提到的,(?<=(?<A>.)(?<-A>.))永远不匹配。
但后来我想,怎么(?<=(?<-A>.)(?<A>.))办?它确实匹配!
怎么样(?<=(?<A>.)(?<A>.))?匹配"12",A是 captures "1",如果我们查看Captures集合,它是{"2", "1"}- 前两个,然后是一个 - 它是相反的。
因此,在向后看时,.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
于 2012-11-16T23:11:26.937 回答
2

我认为问题在于数据而不是模式。数据具有需要匹配的“发布”项目,例如

(ab (c) 定义)

其中 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    
)

这是使用我自己创建的工具进行的比赛(也许有一天我会发布)。请注意,˽ 显示匹配空格的位置。

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
于 2012-11-15T04:37:20.593 回答