Да, это возможно при использовании регулярных выражений двигатель, поддерживает обратные ссылки и условия.
Во-первых, список последовательных чисел можно разложить в список, где каждая пара чисел являются последовательными:
(?=(?&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
Хорошее сообщение. У меня есть общий вопрос относительно _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
@sln Извините, не знакомы с двигателем regex boost. Если я правильно понимаю, проблема в том, что boost не поддерживает backrefs для групп, которые еще не были (полностью) согласованы, правильно? В этом случае ваше решение кажется разумным. Я также добавил альтернативное решение, основанное на рекурсивных ссылках на подшаблон, возможно, это работает из коробки ... – NikiC
Это время компиляции во время разбора: 'групп, которые еще не были (полностью) проанализированы'. Поэтому ссылка на его родительскую группу захвата. Я решил проблему, разрешив backrefs этой отметке в момент открытия группы, но до того, как внутренние состояния будут добавлены через рекурсивный синтаксический анализ. Раньше он допускал только обратную привязку к этой отметке при анализе закрывающего ')'. Это простая битовая маска 'm_backrefs | = 1 << (markid-1)', если закрывающий пар не найден, генерируется ошибка, и все это в любом случае раскручивается. Работает так, как ожидалось. Я просто добавляю это к другим модам, которые я сделал. – sln