Я знаю, что это действительно старый вопрос, и вы, вероятно, больше не заинтересованы в решении самостоятельно, но я считаю, что существующий ответ вводит в заблуждение, и хотел предоставить еще один. (Окончательное решение в другом ответе отлично подходит для данной проблемы - вам не нужно решать все с помощью одного регулярного выражения. Но проблема, указанная в вашем ответе, на самом деле не была проблемой.)
Ваша идея была на самом деле правильной, но реализация нет. Первая проблема заключается в том, что вы используете положительный ретроспективный анализ, который гарантирует, что то же самое значение появлялось ранее. Вам нужен отрицательный просмотр назад, (?<!...)
чтобы убедиться, что значение не появилось.
Однако основная проблема заключается в том, что порядок содержимого вашего просмотра назад неверен. В вашем коде сначала есть подстановочный знак .*?
, а затем то, что вы хотите сопоставить с tag (\k<ID>)
. Но вы ищете tag X
где-то слева от вашей текущей позиции, что означает, что подстановочный знак на самом деле должен идти вторым. При просмотреназад текущая позиция механизма регулярных выражений (вне просмотра назад) находится в конце внутреннего шаблона. Представьте, что вы делаете (?<=abc)def
. Это соответствует def
в abcdef
, но не соответствует cbadef
. (Более того, если .*?
должен был идти впереди, почему бы самому tag (\k<ID>)
не перевернуться, как (\k<ID> gat)
?
Итак, это отлично работает:
(\.\.\. (?<ID>(\d+)))(?<!(?s)tag (\k<ID>).*?)
Хотя в нем намного больше групп, чем вам на самом деле нужно. Это достаточно:
\.\.\. (?<ID>\d+)(?<!(?s)tag \k<ID>.*?)
Протестируйте здесь.
Что касается комментария Саймона: «Двигатель регулярных выражений .NET анализирует символы слева направо, поэтому вы не можете ссылаться справа налево». Я не знаю об этом. Я не видел кода движка регулярных выражений, но после многих лет экспериментов с разновидностью регулярных выражений .NET и злоупотребления им для всевозможных махинаций я могу сказать, что он, безусловно, похоже отступает, как и следовало ожидать для все практические цели. (Более быстрые алгоритмы, на которые он ссылался, применяются только к шаблонам, которые не используют обратные ссылки, где результаты совершенно неразличимы.) Да, шаблоны сопоставляются слева направо. Но ретроспективный просмотр не обрабатывается до тех пор, пока вы не достигнете ретроспективного просмотра в шаблоне, и он может ссылаться на более ранние захваты, даже если они заканчиваются позже в совпадении.
Два предостережения по этому поводу: а) .NET имеет режим сопоставления справа налево, который фактически обрабатывает шаблон справа налево, поэтому захваты должны появляться после их обратных ссылок в коде и б ) просмотр назад использует указанный режим чтения справа налево (это, к сожалению, не задокументировано), так что движок может на самом деле перемещаться вперед и назад по входной строке, используя просмотр назад в обычных шаблонах слева направо (или просмотр вперед в боковом направлении справа налево). шаблоны -left или просмотр вперед внутри просмотра назад, а что нет, но это, вероятно, не то, что вы хотите использовать в рабочем коде).
Это направление обработки ввода всегда важно при рассмотрении того, когда обратные ссылки допустимы, а когда нет.
person
Martin Ender
schedule
16.02.2016