Закаленный жадный токен - что отличает размещение точки до негативного взгляда

<table((?!</table>).)*</table>

соответствует всем моим тегам таблицы, однако

<table(.(?!</table>))*</table>

нет. Второй, кажется, имеет смысл, если я попытаюсь выписать выражение в словах, но я не могу понять первый.

Может кто-нибудь объяснить мне эту разницу?

Для справки я получил здесь термин "Закаленный жадный токен": http://www.rexegg.com/regex-quantifiers.html#tempered_greed

Ответ 1

Так как Google возвращает этот вопрос SO поверх результатов для tempered greedy token, я считаю необходимым предоставить более полный ответ.

Что такое закаленный жадный токен?

Ссылка rexegg.com умеренная лексика довольно кратка:

В (?:(?!{END}).)* квантор * применяется к точке, но теперь она является закаленной точкой. Отрицательный lookahead (?!{END}) утверждает, что то, что следует за текущей позицией, не является строкой {END}. Поэтому точка никогда не сможет сопоставить открывающую скобку {END}, гарантируя, что мы не будем перепрыгивать через разделитель {END}.

Вот он: умеренный жадный токен - это своего рода отрицательный класс символов для последовательности символов (см. отрицательный класс символов для одного символа).

ПРИМЕЧАНИЕ. Разница между умеренным жадным токеном и отрицательным символьным классом заключается в том, что первый не соответствует тексту, отличному от самой последовательности, а единственный символ, который не запускает эту последовательность, То есть (?:(?!abc|xyz).)+ не будет соответствовать def в defabc, но будет соответствовать def и bc, потому что a запускается запрещенная последовательность abc, а bc - нет.

Он состоит из:

  • (?:...)* - количественная группа, не захватывающая захват (это может быть группа захвата, но бессмысленно захватывать каждый отдельный символ) (a * может быть +, это зависит от того, пустая строка матч ожидается)
  • (?!...) - отрицательный результат, который фактически налагает ограничение на значение справа от текущего местоположения
  • . - (или любой (обычно один) символ) шаблон потребления.

Тем не менее, мы всегда можем смягчить токен, используя чередование в отрицательном образе (например, (?!{(?:END|START|MID)})) или заменяя точку совпадения с помощью символа отрицательного символа (например, (?:(?!START|END|MID)[^<>]) при попытке сопоставить текст только внутри теги).

Размещение расходной части

Обратите внимание, что не упоминается конструкция, в которой потребляемая часть (точка в исходном закаленном жадном токене) помещается перед записью. Ответ Avinash ясно объясняет эту часть: (.(?!</table>))* сначала соответствует любому символу (но новой строке без модификатора DOTALL), а затем проверяет, не следует ли ему </table>, что приводит к несоответствию e в <table>table</table>. Потребляющая часть (.) ДОЛЖНА быть размещена после просмотра заката.

Когда использовать умеренный токен?

Rexegg.com дает идею:

  • Если мы хотим сопоставить блок текста между разделителем 1 и разделителем 2 без промежуточной подстроки 3 (например, {START}(?:(?!{(?:MID|RESTART)}).)*?{END}
  • Когда мы хотим сопоставить блок текста, содержащий конкретный шаблон внутри, без переполнения последующих блоков (например, вместо ленивого совпадения точек, как в <table>.*?chair.*?</table>, мы будем использовать что-то вроде <table>(?:(?!chair|</?table>).)*chair(?:(?!<table>).)*</table>).
  • Если мы хотим совместить кратчайшее окно между двумя строками. Lazy matching не поможет, когда вам нужно получить abc 2 xyz от abc 1 abc 2 xyz (см. abc.*?xyz и abc(?:(?!abc).)*?xyz).

Ошибка производительности

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

Скажем, мы хотим сопоставить abc 2 xyz в abc 1 abc 2 xyz 3 xyz. Вместо проверки каждого символа между abc и xyz с помощью abc(?:(?!abc|xyz).)*xyz мы можем пропустить все символы, которые не являются a или x с [^ax]*, а затем сопоставить все a, за которыми не следует bca(?!bc)) и все x, за которыми не следует yzx(?!yz)): abc[^ax]*(?:a(?!bc)[^ax]*|x(?!yz)[^ax]*)*xyz.

Ответ 2

((?!</table>).)* будет проверять, что этот конкретный символ, который будет согласован, не должен быть начальным символом в строке </table>. Если да, то только он соответствует этому конкретному символу. * повторяет то же самое ноль или более раз.

(.(?!</table>))* соответствует любому символу, только если за ним не следует </table>, ноль или более раз. Таким образом, это будет соответствовать всем символам внутри тега таблицы excpet последнего символа, так как за последним char следует </table>. И следующий шаблон </table> утверждает, что в конце матча должен быть тег закрывающей таблицы. Это приведет к сбою совпадения.

Смотрите здесь

Ответ 3

Умеренный жадный токен на самом деле просто означает:

", но только до точки"

как вы это делаете:

вы помещаете токен, который вы не хотите сопоставлять, как негативный (?!notAllowedToMatch) перед точкой . (соответствует любому вещь), то вы повторяете эту вещь со звездой *:

((?!notAllowedToMatch).)*

как это работает:

"и снова и снова используйте один, перемещая один символ во времени слева направо через входную строку до тех пор, пока не будет видна запрещенная последовательность (или конец строки), на которой точка остановки матча.

Wiktor более подробный ответ хорош, я просто подумал, что более простое объяснение было в порядке.