简体   繁体   English

正则表达式以验证逗号分隔的唯一编号列表

[英]Regex to validate a comma separated list of unique numbers

I am trying to validate a comma separated list of numbers 1-7 unique (not repeating). 我正在尝试验证以逗号分隔的数字1-7唯一列表(不重复)。

ie

  • 2,4,6,7,1 is valid input. 2,4,6,7,1是有效输入。
  • 2,2,6 is invalid 2,2,6无效
  • 2 is valid 2有效
  • 2, is invalid 2,无效
  • 1,2,3,4,5,6,7,8 is invalid ( only 7 number) 1,2,3,4,5,6,7,8无效(仅7个数字)

I tried ^[1-7](?:,[1-7])*$ but it's accepting repeating numbers 我尝试了^[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/>'); }); 

Regex are not suited for this. 正则表达式不适用于此。 You should split the list into an array and try the different conditions: 您应该将列表分成一个数组,然后尝试不同的条件:

 function isValid(list) { var arrList = list.split(","); if (arrList.length > 7) { // if more than 7, there are duplicates return false; } var temp = {}; for (var i in arrList) { if (arrList[i] === "") return false; // if empty element, not valid temp[arrList[i]] = ""; } if (Object.keys(temp).length !== arrList.length) { // if they're not of same length, there are duplicates return false; } return true; } console.log(isValid("2,4,6,7,1")); // true console.log(isValid("2,2,6")); // false console.log(isValid("2")); // true console.log(isValid("2,")); // false console.log(isValid("1,2,3,4,5,6,7,8")); // false console.log(isValid("1,2,3")); // true console.log(isValid("1,2,3,7,7")); // false 

No RegEx is needed: 无需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

You were pretty close. 你很近。

 ^                    # 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/>'); }); 

Output 产量

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

For a number range that exceeds 1 digit, just add word boundary's around 对于超过1位的数字范围,只需添加单词边界
the capture group and the back reference. 捕获组和反向引用。
This isolates a complete number. 这样可以隔离出一个完整的数字。

This particular one is numb range 1-31 这个特定的麻木范围是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/>'); }); 

Edit: 编辑:

Fixed error when repeating digit wasn't the first one. 修复了重复数字不是第一个数字时的错误。


One way of doing it is: 一种方法是:

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

It captures a digit and then uses a uses a look-ahead to make sure it doesn't repeats itself. 它捕获一个数字,然后使用“预读”以确保它不会重复。

^                               # 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/>'); }); 

Note! 注意! Don't know if performance is an issue, but this does it in almost half the number of steps compared to sln's solution ;) 不知道性能是否会成为问题,但是与sln的解决方案相比,这几乎可以完成一半的步骤;)

Like other commenters, I recommend you to use something other than regular expressions to solve your problem. 与其他评论者一样,我建议您使用除正则表达式之外的其他方法来解决您的问题。

I have a solution, but it is too long to be a valid answer here (answers are limited to 30k characters). 我有一个解决方案,但是在这里不能作为有效答案太长(答案限于30k个字符)。 My solution is actually a regular expression in the language-theory sense, and is 60616 characters long. 我的解决方案实际上是语言理论意义上的正则表达式 ,长度为60616个字符。 I will show you here the code I used to generate the regular expression, it is written in Python, but easily translated in any language you desire. 我将在这里向您展示我用来生成正则表达式的代码,该代码是用Python编写的,但可以轻松地翻译为所需的任何语言。 I confirmed that it is working in principle with a smaller example (that uses only the numbers 1 to 3): 我确认它在原理上是一个较小的示例(仅使用数字1到3):

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

Here's the code used to generate the regex: 这是用于生成正则表达式的代码:

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

Call it like this: 这样称呼它:

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

The concept is the following: 概念如下:

  • To match a single number a , we can use the simple regex /a/ . 要匹配单个数字a ,我们可以使用简单的正则表达式/a/
  • To match two numbers a and b , we can match the disjunction /(a(,b)?|b(,a)?)/ 要匹配两个数字ab ,我们可以匹配析取项/(a(,b)?|b(,a)?)/
  • Similarily, to match n numbers, we match the disjunction of all elements, each followed by the optional match for the subset of size n-1 not containing that element. 类似地,为了匹配n数字,我们匹配所有元素的析取,每个元素后面都跟不包含该元素的大小为n-1的子集的可选匹配。
  • Finally, we wrap the expression in ^...$ in order to match the entire text. 最后,我们将表达式包装在^...$中以匹配整个文本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM