Я пытаюсь проверить список цифр 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/>');
  });
2
Federico Vicente 29 Авг 2017 в 17:34

5 ответов

Лучший ответ

Вы были довольно близко

 ^                    # 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/>');
      });
1
29 Авг 2017 в 18:56

RegEx не требуется:

Это гораздо проще и понятнее, чем было бы сложное регулярное выражение.

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
1
29 Авг 2017 в 16:05

Как и другие комментаторы, я рекомендую вам использовать что-то кроме регулярных выражений для решения вашей проблемы.

У меня есть решение, но оно слишком длинное, чтобы быть здесь верным ответом (ответы ограничены 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/.
  • Чтобы сопоставить два числа a и b, мы можем сопоставить дизъюнкцию /(a(,b)?|b(,a)?)/
  • Аналогично, чтобы сопоставить числа n, мы сопоставляем дизъюнкцию всех элементов, за каждым из которых следует необязательное совпадение для подмножества размера n-1, не содержащего этот элемент.
  • Наконец, мы заключаем выражение в ^...$, чтобы соответствовать всему тексту.
0
L3viathan 29 Авг 2017 в 18:10

Regex не подходят для этого. Вы должны разбить список на массив и попробовать разные условия:

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
3
ishegg 29 Авг 2017 в 15:05

< Сильный > Edit:

Исправлена ошибка, когда повторяющаяся цифра была не первой.


Один из способов сделать это:

^(?:(?:^|,)([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;)

0
SamWhan 30 Авг 2017 в 10:40