正则表达式:逗号分隔的唯一数字

3

我正在尝试验证一个逗号分隔的数字列表,范围为1-31,不能重复。

例如:

  • 2,4,6,7,1 是有效输入。

  • 2,2,6 是无效的

  • 2 是有效的

  • 2, 是无效的

  • 1,2,3,4,15,6,7,31 是有效的

  • 1,2,3,4,15,6,7,32 是无效的

  • 20,15,3

    我尝试过 ^((([0]?[1-9])|([1-2][0-9])|(3[01]))(?!([0]?[1-9])|([1-2][0-9])|(3[01])*,\\1(?!([0]?[1-9])|([1-2][0-9])|(3[01])) 但它允许重复的数字


2
为什么不通过定界符拆分数字列表,然后在可迭代对象中检查重复项呢? - ctwheels
你的列表中是否有一个固定数量的数字,不能超过这个数量?无论如何,我建议您将问题分解,而不是在一个正则表达式中处理所有内容:也许使用一个正则表达式来验证逗号分隔格式是否正确,然后在逗号处拆分,并通过循环遍历哈希集合来验证数字是否唯一。 - Austin_Anderson
这感觉像是将其拆分为列表并在其中执行操作会更好,不是正则表达式的好用法。 - sniperd
你使用的正则表达式引擎是哪个? - anubhava
我不认为正则表达式在这里有用。 你在使用什么编程语言? - Youssef13
3个回答

3
对于超过1位数的数字范围,只需要在捕获组和反向引用周围添加单词边界即可。这样会隔离整个数字。
这个特定的例子是数字范围1-31。
 ^                                       # BOS
 (?!                                     # Validate no dups
      .* 
      (                                       # (1 start)
           \b 
           (?: [1-9] | [1-2] \d | 3 [0-1] )        # number range 1-31
           \b 
      )                                       # (1 end)
      .* 
      \b \1 \b 
 )
 (?: [1-9] | [1-2] \d | 3 [0-1] )        # Unrolled-loop, match 1 to many numb's
 (?:                                     # in the number range 1-31
      , 
      (?: [1-9] | [1-2] \d | 3 [0-1] )
 )*
 $                                       # EOS

    var data = [
      '2,4,6,7,1',
      '2,2,6',
      '2,30,16,3',
      '2,',
      '1,2,3,2',
      '1,2,2,3',
      '1,2,3,4,5,6,7,8'
      ];
      
      data.forEach(function(str) {
        document.write(str + ' gives ' + /^(?!.*(\b(?:[1-9]|[1-2]\d|3[0-1])\b).*\b\1\b)(?:[1-9]|[1-2]\d|3[0-1])(?:,(?:[1-9]|[1-2]\d|3[0-1]))*$/.test(str) + '<br/>');
      });


你真是个天才。再次感谢。如果您有任何教程,我会非常感激。 - Federico Vicente

2

我完全同意,寻找重复项比正则表达式有更好的方法,但如果你必须使用正则表达式,这里有一种方法(取决于你的正则表达式风格)。

请参见regex101(我已将其设置为多行和扩展,仅供测试和可读性)。

^
(?!.*\b(\d+)\b.*\b\1\b)
(0?[1-9]|[12][0-9]|3[01])
(,(0?\d|[12][0-9]|3[01]))*
$

说明:

  • (?!.*\b(\d+)\b.*\b\1\b)是一个负向先行断言,用于确保没有重复的数字
  • (0?[1-9]|[12][0-9]|3[01])匹配第一个数字
  • (,(0?\d|[12][0-9]|3[01]))*匹配任何多余的数字

注意:已更新为使用单词边界 - 基于 @sln 的答案


2
我已经创建了一个可以做到这一点的模式。
该模式为:^((?!(\d+),[^\n]*\b\2\b)([1-9]\b|[1-2]\d|3[0-1])(,(?1))?)$ 这里有一个演示。
简短的描述:
  • ^ - 匹配行首。
  • (?!(\d+),[^\n]*\b\2\b) - 正向预查确保下一个数字不会重复
    • (\d+) - 获取下一个数字
    • ,[^\n]* - 逗号后面跟着任何非换行符
    • \b\2\b - 再次重复下一个数字
  • ([1-9]\b|[1-2]\d|3[0-1]) - 检查下一个数字是否在1-31之间
    • [1-9]\b - 检查单个数字。使用边界来防止匹配两位数。
    • [1-2]\d - 检查10-29
    • 3[0-1] - 检查30,31
  • (,(?1))?) 如果后面跟着逗号,则在主模式上递归,检查下一个数字是否重复。
  • , - 检查后面是否跟着逗号。
  • (?1) - 在主模式上递归。
  • $ - 行尾。
更新:忘记检查1-31。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接