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

时间:2012-11-15 00:04:17

标签: .net regex lookaround balancing-groups

  

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

我试图找到this other question的答案,作为借用.NET平衡组的借口。但是,我不能让它们在可变长度的后视镜中工作。

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

考虑这个输入:

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

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

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

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

~(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替换ai之间的字母以及失败后的所有案例。

那么(可变长度)外观是什么打破了平衡组的使用?我整个晚上都试着研究这个问题(并且找到了像this one这样的页面),但我在后面找不到这个用途。

我也很高兴,如果有人可以将我与一些关于.NET正则表达式引擎如何在内部处理特定于.NET的功能的深入信息联系起来。我找到this amazing article,但它似乎没有进入(可变长度)的lookbehinds,例如。

2 个答案:

答案 0 :(得分:12)

我想我明白了 首先,正如我在其中一条评论中提到的那样,(?<=(?<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

答案 1 :(得分:2)

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

(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&amp;应该忽略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