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

Nik*_*kiC 11 regex pcre

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

这些应匹配:

0,1,2,3
8,9,10,11
1999,2000,2001
99,100,101
Run Code Online (Sandbox Code Playgroud)

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

42
3,2,1
1,2,4
10,11,13
Run Code Online (Sandbox Code Playgroud)

Nik*_*kiC 22

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

首先,连续数字列表可以分解为一对列表,其中每对数字都是连续的:

(?=(?&cons))\d+
(?:,(?=(?&cons))\d+)*
,\d+
Run Code Online (Sandbox Code Playgroud)

(?=(?&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)
Run Code Online (Sandbox Code Playgroud)

对于简要说明,第二种案例处理999,1000类型对更容易理解 - 在关于匹配^ nb ^ 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)
Run Code Online (Sandbox Code Playgroud)

第一个块将捕获数字是否为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)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,两个语法9^n ,1 0^n,n> = 1和prefix N 9^n , prefix N+1 0^n,n> = 0几乎只是明确写出来的.

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