捕捉量词和量词算术

时间:2014-04-10 23:52:47

标签: regex recursion quantifiers

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

捕捉量词

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

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

您可以想象一种语法,例如

L(+)R{\q1}

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

这对平衡字符串中{@,=, - ,/}的数量很有用 @@@@"星球大战" ====" 1977" ----"科幻小说" ////" George Lucas"

与递归的关系

在某些情况下,量词捕获会优雅地替换递归,例如由相同数量的Ls和Rs构成的一段文本,a

L(+) some_content R{\q1} 

这个想法在以下页面的一些细节中提供:Captured Quantifiers

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

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

提前感谢您的见解!!!

  

更新

     

Casimir给出了一个很棒的答案,显示了两种方法来验证模式的各个部分具有相同的长度。但是,我不想在日常工作中依赖其中任何一种。这些都是表现出色表演的伎俩。在我看来,这些美丽但复杂的方法证实了这个问题的前提:一个正则表达式特征来捕获量化的字符数(例如+或*)能够匹配将使这种平衡模式非常简单并扩展语法一种令人愉快的表达方式。

     

更新2 (稍后)

     

我发现.NET的功能与我的要求非常接近。添加了演示该功能的答案。

2 个答案:

答案 0 :(得分:12)

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

@@@@ "Star Wars" ==== "1977" ---- "Science Fiction" //// "George Lucas"
您可以检查@ = - /是否与此模式保持平衡使用the famous Qtax trick,(你准备好了吗?):“占有 - 可选的自引用组”

~(?<!@)((?:@(?=[^=]*(\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?

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

请注意,可以看到这样的组:如果组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。如果你想获得整个字符串作为匹配结果,你必须在最后添加.*(否则匹配结果将为空,因为有趣的注意到它。)

答案 1 :(得分:2)

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

这是我提出的解决方案。它看起来很长,但很简单。

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

它是如何运作的?

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

  2. 接下来,[^@=]+会占用第一个=之前的所有字符。

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

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

  5. [^/]+会占用其余字符串。

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

  7. 同样,(?(c2)(?!))(?(c3)(?!))确保不会有@-/个字符。

    < / LI>

    我不了解你,但即使这有点长,我发现它非常直观。