2016-09-03 4 views
11

Можно ли сопоставить список десятичных целых чисел, разделенных запятыми, где целые числа в списке всегда увеличиваются на единицу?Список совпадений целых чисел с использованием regex

Они должны соответствовать:

0,1,2,3 
8,9,10,11 
1999,2000,2001 
99,100,101 

Они не должны совпадать (во всей их полноте - две последние имеют соответствующие подпоследовательности):

42 
3,2,1 
1,2,4 
10,11,13 

ответ

21

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

Во-первых, список последовательных чисел можно разложить в список, где каждая пара чисел являются последовательными:

(?=(?&cons))\d+ 
(?:,(?=(?&cons))\d+)* 
,\d+ 

Здесь (?=(?&cons)) является заполнителем для предиката, который гарантирует, что два числа подряд. Этот предикат может выглядеть следующим образом:

(?<cons>\b(?: 
    (?<x>\d*) 
    (?:(?<a0>0)|(?<a1>1)|(?<a2>2)|(?<a3>3)|(?<a4>4) 
       |(?<a5>5)|(?<a6>6)|(?<a7>7)|(?<a8>8)) 
    (?:9(?= 9*,\g{x}\d (?<y>\g{y}?+ 0)))* 
    ,\g{x} 
    (?(a0)1)(?(a1)2)(?(a2)3)(?(a3)4)(?(a4)5) 
    (?(a5)6)(?(a6)7)(?(a7)8)(?(a8)9) 
    (?(y)\g{y}) 
    # handle the 999 => 1000 case separately 
    | (?:9(?= 9*,1 (?<z>\g{z}?+ 0)))+ 
    ,1\g{z} 
)\b) 

Для краткого объяснения, вторая обработка 999,1000 пар типа случае легче понять - есть очень подробное описание того, как это работает в this answer concerned with matching a^n b^n. Связь между ними заключается в том, что в этом случае мы должны соответствовать 9^n ,1 0^n.

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

(?:(?<a0>0)|(?<a1>1)|(?<a2>2)|(?<a3>3)|(?<a4>4) 
      |(?<a5>5)|(?<a6>6)|(?<a7>7)|(?<a8>8)) 

(?(a0)1)(?(a1)2)(?(a2)3)(?(a3)4)(?(a4)5) 
(?(a5)6)(?(a6)7)(?(a7)8)(?(a8)9) 

Первый блок будет захватывать ли цифра N в группу, а второй Затем блок будет использовать условные обозначения, чтобы проверить, какая из этих групп была использована. Если группа aN непустая, следующая цифра должна быть N + 1.

Остальная часть первого корпуса обрабатывает такие случаи, как 1999,2000. Это снова попадает в шаблон N 9^n, N+1 0^n, поэтому это комбинация метода для сопоставления a^n b^n и приращения десятичной цифры. Простой случай 1,2 обрабатывается как предельный случай, когда n = 0.

Полное регулярное выражение: https://regex101.com/r/zG4zV0/1


В качестве альтернативы (?&cons) предикат может быть реализовано несколько более непосредственно, если рекурсивные ссылки подшаблонов поддерживаются:

(?<cons>\b(?: 
    (?<x>\d*) 
    (?:(?<a0>0)|(?<a1>1)|(?<a2>2)|(?<a3>3)|(?<a4>4) 
       |(?<a5>5)|(?<a6>6)|(?<a7>7)|(?<a8>8)) 
    (?<y> 
     ,\g{x} 
     (?(a0)1)(?(a1)2)(?(a2)3)(?(a3)4)(?(a4)5) 
     (?(a5)6)(?(a6)7)(?(a7)8)(?(a8)9) 
     | 9 (?&y) 0 
    ) 
    # handle the 999 => 1000 case separately 
    | (?<z> 9,10 | 9(?&z)0) 
)\b) 

В этом случае два грамматик 9^n ,1 0^n, п> = 1 и prefix N 9^n , prefix N+1 0^n, n> = 0 в значительной степени просто выписаны явно.

Полное альтернативное регулярное выражение: https://regex101.com/r/zG4zV0/3

+0

Хорошее сообщение. У меня есть общий вопрос относительно _nested back reference_ Это выражение в вашем регулярном выражении, например '(? \ g {y}? + 0)'. Я использую движок _Boost regex_, который в основном совместим с PCRE и Perl. Иногда мне приходится использовать обходные пути для определенных ситуаций. Один для этого - например '(? (? & _ Y)? + 0) (? (DEFINE) (?<_y> \ g {y})) 'Иногда меня иногда мучает вопрос с сообщением' undefined backref', что, если обходной путь можно сделать, почему бы не поддержать его прямо. Интересно, знаете ли вы лучшее обходное решение, которое не требует вызова функции? (_workaround https://regex101.com/r/zG4zV0/2_). – sln

+0

@sln Извините, не знакомы с двигателем regex boost. Если я правильно понимаю, проблема в том, что boost не поддерживает backrefs для групп, которые еще не были (полностью) согласованы, правильно? В этом случае ваше решение кажется разумным. Я также добавил альтернативное решение, основанное на рекурсивных ссылках на подшаблон, возможно, это работает из коробки ... – NikiC

+0

Это время компиляции во время разбора: 'групп, которые еще не были (полностью) проанализированы'. Поэтому ссылка на его родительскую группу захвата. Я решил проблему, разрешив backrefs этой отметке в момент открытия группы, но до того, как внутренние состояния будут добавлены через рекурсивный синтаксический анализ. Раньше он допускал только обратную привязку к этой отметке при анализе закрывающего ')'. Это простая битовая маска 'm_backrefs | = 1 << (markid-1)', если закрывающий пар не найден, генерируется ошибка, и все это в любом случае раскручивается. Работает так, как ожидалось. Я просто добавляю это к другим модам, которые я сделал. – sln

Смежные вопросы