正则表达式验证逗号分隔的唯一数字列表

时间:2017-08-29 14:34:02

标签: javascript regex

我正在尝试验证逗号分隔的数字列表1-7唯一(不重复)。

即。

  • 2,4,6,7,1是有效输入。
  • 2,2,6无效
  • 2有效
  • 2,无效
  • 1,2,3,4,5,6,7,8无效(仅限7个号码)

我尝试了^[1-7](?:,[1-7])*$,但它正在接受重复的数字



var data = [
  '2,4,6,7,1',
  '2,2,6',
  '2',
  '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 ' + /(?!([1-7])(?:(?!\1).)\1)^((?:^|,)[1-7]){1,7}$/.test(str) + '<br/>');
  });
&#13;
&#13;
&#13;

5 个答案:

答案 0 :(得分:3)

正则表达式不适合这个。您应该将列表拆分为数组并尝试不同的条件:

&#13;
&#13;
Citrus
&#13;
&#13;
&#13;

答案 1 :(得分:1)

不需要RegEx:

This is much more maintainable and explicit than a convoluted regular expression would be.

function isValid(a) {
  var s = new Set(a);
  s.delete(''); // for the hanging comma case ie:"2,"
  return a.length < 7 && a.length == s.size;
}

var a = '2,4,6,7,1'.split(',');
alert(isValid(a)); // true
a = '2,2,6'.split(',');
alert(isValid(a)); // false
a = '2'.split(',');
alert(isValid(a)); // true
a = '2,'.split(',');
alert(isValid(a)); // false
'1,2,3,4,5,6,7,8'.split(',');
alert(isValid(a)); // false

答案 2 :(得分:1)

你非常接近。

 ^                    # BOS
 (?!                  # Validate no dups
      .* 
      ( [1-7] )            # (1)
      .* 
      \1 
 )
 [1-7]                # Unrolled-loop, match 1 to 7 numb's
 (?:
      , 
      [1-7] 
 ){0,6}
 $                    # EOS

var data = [
  '2,4,6,7,1',
  '2,2,6',
  '2',
  '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 ' + /^(?!.*([1-7]).*\1)[1-7](?:,[1-7]){0,6}$/.test(str) + '<br/>');
  });

输出

2,4,6,7,1 gives true
2,2,6 gives false
2 gives true
2, gives false
1,2,3,2 gives false
1,2,2,3 gives false
1,2,3,4,5,6,7,8 gives false

对于超过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 7 numb's
 (?:                                     # in the number range 1-31
      , 
      (?: [1-9] | [1-2] \d | 3 [0-1] )
 ){0,6}
 $                                       # 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])){0,6}$/.test(str) + '<br/>');
      });

答案 3 :(得分:0)

修改

重复数字不是第一个时修正了错误。

这样做的一种方法是:

^(?:(?:^|,)([1-7])(?=(?:,(?!\1)[1-7])*$))+$

它捕获一个数字,然后使用一个预测来确保它不会重复。

^                               # Start of line
    (?:                         # Non capturing group
        (?:                     # Non capturing group matching:
            ^                   #  Start of line
            |                   # or
            ,                   #  comma
        )                       #
        ([1-7])                 # Capture digit being between 1 and 7
        (?=                     # Positive look-ahead
            (?:                 # Non capturing group
                ,               # Comma
                (?!\1)[1-7]     # Digit 1-7 **not** being the one captured earlier
            )*                  # Repeat group any number of times
            $                    # Up to end of line
        )                       # End of positive look-ahead
    )+                          # Repeat group (must be present at least once)
$                                # End of line

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

请注意!不知道性能是否是一个问题,但与sln的解决方案相比,这几乎可以实现一半的步骤;)

答案 4 :(得分:0)

与其他评论者一样,我建议您使用正则表达式之外的其他内容来解决您的问题。

我有一个解决方案,但在这里做一个有效的答案太长了(答案限于30k字符)。我的解决方案实际上是语言理论意义上的正则表达式,长度为60616个字符。我将在这里向您展示我用来生成正则表达式的代码,它是用Python编写的,但可以用您想要的任何语言轻松翻译。我确认它原则上使用了一个较小的例子(仅使用数字1到3):

^(2(,(3(,1)?|1(,3)?))?|3(,(1(,2)?|2(,1)?))?|1(,(3(,2)?|2(,3)?))?)$

以下是用于生成正则表达式的代码:

def build_regex(chars):
    if len(chars) == 1:
        return list(chars)[0]
    return ('('
    +
    '|'.join('{}(,{})?'.format(c, build_regex(chars - {c})) for c in chars)
    +
    ')')

这样称呼:

'^' + build_regex(set("1234567")) + "$"

概念如下:

  • 要匹配单个号码a,我们可以使用简单的正则表达式/a/
  • 要匹配两个号码ab,我们可以匹配分离/(a(,b)?|b(,a)?)/
  • 类似地,为了匹配n个数字,我们匹配所有元素的析取,每个元素后跟可选匹配的大小n-1的子集不包含该元素。
  • 最后,我们将表达式包装在^...$中以匹配整个文本。