10

首先,让我解释一下,这个问题既不是关于如何捕获组,也不是关于如何使用量词,这是我非常熟悉的正则表达式的两个特性。对于可能熟悉奇异引擎中不寻常语法的正则表达式爱好者来说,这更像是一个高级问题。

捕获量词

有谁知道正则表达式是否允许您捕获量词?我的意思是,与 + 和 * 等量词匹配的字符数将被计算在内,并且这个数字可以在另一个量词中再次使用。

例如,假设您想确保在这种字符串中具有相同数量的 Ls 和 Rs:LLLRRRRR

你可以想象这样的语法

L(+)R{\q1}

其中 L 的 + 量词被捕获,并且捕获的数字在 R 的量词中被称为 {\q1}

这将有助于平衡字符串中 {@,=,-,/} 的数量,例如 @@@@ "Star Wars" ==== "1977" ---- "Science Fiction" //// "乔治卢卡斯”

与递归的关系

在某些情况下,量词捕获将优雅地取代递归,例如由相同数量的 Ls 和 Rs 框起来的一段文本,a in

L(+) some_content R{\q1} 

这个想法在下一页的一些细节中提出:捕获的量词

它还讨论了对捕获的量词的自然扩展:量词算法,适用于您想要匹配 (3*x + 1) 之前匹配的字符数的情况。

我试图找出是否存在这样的事情。

提前感谢您的见解!!!

更新

Casimir 给出了一个绝妙的答案,展示了两种方法来验证模式的各个部分是否具有相同的长度。但是,我不想在日常工作中依赖其中任何一个。这些确实是展示出色表演技巧的技巧。在我看来,这些漂亮但复杂的方法证实了问题的前提:捕获量词(例如 + 或 *)能够匹配的字符数量的正则表达式功能将使这种平衡模式非常简单,并将语法扩展为一种令人愉悦的表达方式。

更新 2(很久以后)

我发现 .NET 有一个与我所要求的功能接近的功能。添加了一个答案来演示该功能。

4

2 回答 2

13

我不知道可以捕获量词的正则表达式引擎。但是,PCRE 或 Perl 可以使用一些技巧来检查您是否有相同数量的字符。用你的例子:

@@@@ “星球大战” ==== “1977” ---- “科幻小说” //// “乔治卢卡斯”

您可以检查是否与使用著名的 Qtax 技巧的@ = - /这种模式保持平衡,(您准备好了吗?):“拥有-可选的自引用组”

~(?<!@)((?:@(?=[^=]*(\2?+=)[^-]*(\3?+-)[^/]*(\4?+/)))+)(?!@)(?=[^=]*\2(?!=)[^-]*\3(?!-)[^/]*\4(?!/))~

图案细节:

~                          # pattern delimiter
(?<!@)                     # negative lookbehind used as an @ boundary
(                          # first capturing group for the @
    (?:
        @                  # one @
        (?=                # checks that each @ is followed by the same number
                           # of = - /  
            [^=]*          # all that is not an =
            (\2?+=)        # The possessive optional self-referencing group:
                           # capture group 2: backreference to itself + one = 
            [^-]*(\3?+-)   # the same for -
            [^/]*(\4?+/)   # the same for /
        )                  # close the lookahead
    )+                     # close the non-capturing group and repeat
)                          # close the first capturing group
(?!@)                      # negative lookahead used as an @ boundary too.

# this checks the boundaries for all groups
(?=[^=]*\2(?!=)[^-]*\3(?!-)[^/]*\4(?!/))
~

主要思想

非捕获组仅包含一个@. 每次重复该组时,都会在捕获组 2、3 和 4 中添加一个新角色。

所有格-可选自指组

它是如何工作的?

( (?: @ (?= [^=]* (\2?+ = ) .....) )+ )

在第一次出现 @ 字符时,捕获组 2 尚未定义,因此您不能编写类似的东西,(\2 =)这会使模式失败。为了避免这个问题,方法是使反向引用可选:\2?

该组的第二个方面是=匹配的字符数在每次重复非捕获组时递增,因为=每次都添加 an。为了确保这个数字总是增加(或模式失败),所有格量词强制在添加新=字符之前首先匹配反向引用。

请注意,该组可以这样看:如果组 2 存在,则将其与下一个匹配=

( (?(2)\2) = )

递归方式

~(?<!@)(?=(@(?>[^@=]+|(?-1))*=)(?!=))(?=(@(?>[^@-]+|(?-1))*-)(?!-))(?=(@(?>[^@/]+|(?-1))*/)(?!/))~

您需要使用重叠匹配,因为您将多次使用 @ 部分,这就是所有模式都在环顾内的原因。

图案细节:

(?<!@)                # left @ boundary
(?=                   # open a lookahead (to allow overlapped matches)
    (                 # open a capturing group
        @
        (?>           # open an atomic group
            [^@=]+    # all that is not an @ or an =, one or more times
          |           # OR
            (?-1)     # recursion: the last defined capturing group (the current here)
        )*            # repeat zero or more the atomic group
        =             #
    )                 # close the capture group
    (?!=)             # checks the = boundary
)                     # close the lookahead
(?=(@(?>[^@-]+|(?-1))*-)(?!-))  # the same for -
(?=(@(?>[^@/]+|(?-1))*/)(?!/))  # the same for /

与先例模式的主要区别在于,这种模式不关心= -/组的顺序。(但是,您可以轻松地对第一个模式进行一些更改以处理该问题,使用字符类和负前瞻。)

注意:对于示例字符串,更具体地说,您可以用锚(^\A)替换否定的lookbehind。如果你想获得整个字符串作为匹配结果,你必须.*在最后添加(否则匹配结果将是空的,因为好玩会注意到它。)

于 2014-04-11T01:37:36.930 回答
2

五周后回来,因为我了解到.NET 有一些非常接近问题中提到的“量词捕获”的想法。该功能称为“平衡组”。

这是我想出的解决方案。看起来很长,其实很简单。

(?:@(?<c1>)(?<c2>)(?<c3>))+[^@=]+(?<-c1>=)+[^=-]+(?<-c2>-)+[^-/]+(?<-c3>/)+[^/]+(?(c1)(?!))(?(c2)(?!))(?(c3)(?!))

它是如何工作的?

  1. 第一个非捕获组匹配@字符。在那个非捕获组中,我们有三个命名组 c1、c2 和 c3,它们不匹配任何内容,或者更确切地说,匹配空字符串。这些组将用作三个计数器 c1、c2 和 c3。由于 .NET 在量化组时会跟踪中间捕获,因此每次@匹配 an 时,都会将捕获添加到组 c1、c2 和 c3 的捕获集合中。

  2. 接下来,[^@=]+吃掉直到第一个的所有字符=

  3. 第二个量化组(?<-c1>=)+匹配=字符。该组似乎被命名-c1,但-c1不是组名。-c1is.NET 语法将 c1 组的捕获集合中的一个捕获弹出到以太网中。换句话说,它允许我们减少 c1。如果在捕获集合为空时尝试减少 c1,则匹配失败。这确保了我们永远不会有多个=字符@。(稍后,我们必须确保我们不能有多个@字符=。)

  4. 接下来的步骤对-and/字符重复步骤 2 和 3,递减计数器 c2 和 c3。

  5. [^/]+吃掉了其余的字符串。

  6. (?(c1)(?!))是一个条件,表示“如果组 c1 已设置,则失败”。您可能知道这(?!)是强制正则表达式失败的常见技巧。这个条件确保 c1 一直递减到零:换句话说,不能@超过=字符。

  7. 同样,(?(c2)(?!))and(?(c3)(?!))确保不能有@超过-and/字符。

我不了解你,但即使这有点长,我觉得它真的很直观。

于 2014-05-17T20:36:47.653 回答