使用正则表达式匹配递增整数的列表

时间:2016-09-03 11:52:50

标签: regex pcre

是否可以匹配逗号分隔的十进制整数列表,其中列表中的整数总是递增1?

这些应匹配:

0,1,2,3
8,9,10,11
1999,2000,2001
99,100,101

这些不匹配(完整 - 最后两个具有匹配的子序列):

42
3,2,1
1,2,4
10,11,13

1 个答案:

答案 0 :(得分:21)

是的,当使用支持反向引用和条件的正则表达式引擎时,这是可能的。

首先,连续数字列表可以分解为每对数字连续的列表:

(?=(?&cons))\d+
(?:,(?=(?&cons))\d+)*
,\d+

此处(?=(?&cons))是谓词的占位符,可确保两个数字是连续的。该谓词可能如下所示:

(?<cons>\b(?:
    (?<x>\d*)
    (?:(?<a0>0)|(?<a1>1)|(?<a2>2)|(?<a3>3)|(?<a4>4)
               |(?<a5>5)|(?<a6>6)|(?<a7>7)|(?<a8>8))
    (?:9(?= 9*,\g{x}\d (?<y>\g{y}?+ 0)))*
    ,\g{x}
    (?(a0)1)(?(a1)2)(?(a2)3)(?(a3)4)(?(a4)5)
    (?(a5)6)(?(a6)7)(?(a7)8)(?(a8)9)
    (?(y)\g{y})
    # handle the 999 => 1000 case separately
  | (?:9(?= 9*,1 (?<z>\g{z}?+ 0)))+
    ,1\g{z}
)\b)

有关简要说明,处理999,1000类型对的第二个案例更容易理解 - 在this answer concerned with matching a^n b^n中有一个非常详细的说明。两者之间的联系是,在这种情况下,我们需要匹配9^n ,1 0^n

第一种情况稍微复杂一些。它的最大部分处理递增十进制数字的简单情况,由于所述数字的数量而相对冗长:

(?:(?<a0>0)|(?<a1>1)|(?<a2>2)|(?<a3>3)|(?<a4>4)
           |(?<a5>5)|(?<a6>6)|(?<a7>7)|(?<a8>8))

(?(a0)1)(?(a1)2)(?(a2)3)(?(a3)4)(?(a4)5)
(?(a5)6)(?(a6)7)(?(a7)8)(?(a8)9)

第一个块将捕获数字是否为N到组aN,然后​​第二个块将使用条件来检查使用了哪些组。如果组aN非空,则下一个数字应为N + 1。

第一个案例的其余部分处理像1999,2000这样的案例。这再次落入模式N 9^n, N+1 0^n,因此这是匹配a^n b^n和递增十进制数字的方法的组合。 1,2的简单情况作为极限情况处理,其中n = 0。

完整的正则表达式:https://regex101.com/r/zG4zV0/1

或者,如果支持递归子模式引用,则可以稍微更直接地实现(?&cons)谓词:

(?<cons>\b(?:
    (?<x>\d*)
    (?:(?<a0>0)|(?<a1>1)|(?<a2>2)|(?<a3>3)|(?<a4>4)
               |(?<a5>5)|(?<a6>6)|(?<a7>7)|(?<a8>8))
    (?<y>
        ,\g{x}
        (?(a0)1)(?(a1)2)(?(a2)3)(?(a3)4)(?(a4)5)
        (?(a5)6)(?(a6)7)(?(a7)8)(?(a8)9)
      | 9 (?&y) 0
    )
    # handle the 999 => 1000 case separately
  | (?<z> 9,10 | 9(?&z)0 )
)\b)

在这种情况下,两个语法9^n ,1 0^n,n&gt; = 1和prefix N 9^n , prefix N+1 0^n,n&gt; = 0几乎都是明确写出来的。

完成替代正则表达式:https://regex101.com/r/zG4zV0/3