Повторное повторное повторное повторение

Я пытаюсь проверить текстовую строку с помощью regex.

1,3,4,5,8,10,12,14,19,14

Здесь цифры делятся на "," и должны быть не-появляющимися и меньше или равно 20. А также любое число не должно повторяться. Вот мой шаблон.

^(?:(?:0[1-9]|[1-9]|1[0-9]|20),)*(?:0[1-9]|[1-9]|1[0-9]|20)$

Но он не может проверить повторение. Как я могу проверить его?

Ответ 1

То, что вы хотите сделать, не так сложно. Вам просто нужно проверить после каждого совпадающего номера, если это число снова встречается в строке:

^(?:(0[1-9]|[1-9]|1[0-9]|20),(?!.*\b\1\b))*(?:0[1-9]|[1-9]|1[0-9]|20)$

Посмотрите и протестируйте здесь, в Regexr.

В С#:

string[] myStrings = { "1",
    "1,2",
    "01,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20",
    "01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20",
    "01,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,5",
    "01,02,03,04,05,06,07,08,13,09,10,11,12,13,14,15,16,17,18,19,20" };

Regex reg = new Regex(
    @"^
        (?:(0[1-9]|[1-9]|1[0-9]|20),
            (?!.*\b\1\b) # Fail if the before matched number occurs once more
        )*
        (?:0[1-9]|[1-9]|1[0-9]|20)
    $",
    RegexOptions.IgnorePatternWhitespace
);

foreach (string myString in myStrings)
    Console.WriteLine("{0} {1} a valid string.",
        myString,
        reg.IsMatch(myString) ? "is" : "is not"
    );

Console.ReadLine();

Ответ 2

Как вы отметили свой вопрос как с С#, так и с Java, я не собираюсь давать вам решение для кода здесь, но основная идея.

Если вы разделите строку на ,, вы получите список подстрок: "1", "3" , "4", "5", "8", "10", "12", "14", "19", "14". Теперь вы можете просто перебрать их и попытаться разобрать каждый как целое. Если он терпит неудачу, это не будет число. И если это удастся, вы можете легко проверить, есть ли он < 0 или > 20. И вы также можете сохранить набор номеров, который у вас уже был, и проверить, не повторялся ли текущий.

Суть в том, что вы не должны пытаться использовать регулярные выражения для всего. И ваше языковое требование не является regular в любом случае (если вам нужно запомнить материал или считать вещи, это обычно не является регулярным). Perl на основе RegExps способны немного больше, чем просто регулярно, но этого здесь недостаточно.

Решение как регулярное выражение

Как вы сказали в комментариях, одна строка ограничена не более чем 20 номерами. Поскольку каждое число также ограничено от нуля до двадцати, у вас есть ограниченное количество возможностей для того, как линия может реально выглядеть. Таким образом, у вас есть конечный язык (с конечным числом возможных линий). Конечные языки являются подмножеством регулярных языков и как таковые, вы можете "легко" представлять язык с регулярными выражениями.

Простейшим решением было бы просто перечислить каждую возможную строку. Итак, если у вас было всего 3 номера на строку с 5 наивысшим числом (просто чтобы все было просто), регулярное выражение могло бы выглядеть так:

0,1,2|0,1,3|0,1,4|0,1,5|0,2,3|0,2,4|0,2,5|0,3,4|0,3,5|0,4,5|1,2,3|1,2,4|1,2,5|1,3,4|1,3,5|1,4,5|2,3,4

Конечно, вы могли бы значительно упростить это (возможно, даже больше):

0,(1,(2|3|4|5)|2,(3|4|5)|3,(4|5)|4,5)|1,(2,(3|4|5)|3,(4|5)|4,5)|2,(3,(4|5)|4,5)|3,4,5

Но да, если у вас есть требования, которые делают язык конечным, он также становится регулярным, но не обязательно красивым; и я бы сказал, что "ручное" решение все еще намного читаемо и особенно гибкое.

Ответ 3

Regex - не лучший вариант для этого. Он становится слишком волосатым для повторения чисел. Возможно, вы захотите взглянуть на токенизацию. Даже простые вещи, такие как поиск шаблона, который НЕ присутствует, трудно (см. Регулярное выражение для соответствия строке, которая не содержит слова? для примера)

Я бы разделил строку на commmas, а затем добавлю их в упорядоченный список. Если используется С#:

"1,2,3,4".Split(',')

чтобы начать, продолжите с Linq, чтобы убедиться, что ваши условия выполнены.

Если вы ДОЛЖНЫ сделать это с помощью регулярного выражения, посмотрите на повторение результатов поиска коллекции. Но это покупает вас очень мало над решением выше.

Ответ 4

String[] numbers = input.split(",");
Set<Integer> filtered = new TreeSet();

for(String number: numbers) {
   if(!number.startsWith("-") {
      int nbr = Integer.parseInt(number);

      if(nbr < 20) {
         filtered.add(nbr);
      }
   }
}
for(int nbr: filtered) {
   System.out.print(nbr + " ");
}

Ответ 5

Поскольку вы хотите регулярное выражение, да, вы будете ограничены обратными ссылками, поскольку они идут только от \1 до\9. Поэтому вам нужно исключить пары. Ваша самая большая проблема - избавиться от повторяющихся чисел.

из http://www.regular-expressions.info/refadv.html

используйте (?:(\d?\d),?)+ с (?!<regex>), чтобы убедиться, что у вас нет дубликатов. Вы также можете использовать (?(?=<regex>)true|false)

Я использовал эту страницу для эксперимента: http://www.regextester.com/