Regex заменить текст, но исключить, когда текст находится между определенным тегом

У меня есть следующая строка:

Lorem ipsum Test dolor sit amet, consetetur sadipscing elitr, sed diam nonumy <a href="#" onclick="location.href='http://Test.com/url'; return false;">Test</a> eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd sed Test dolores et ea rebum. Stet clita kasd gubergren, no sea <a href="#" onclick="location.href='http://url.com'; return false;">Test xyz</a> takimata sanctus est Lorem ipsum dolor sit amet.

Теперь я бы заменил строку "Test" вне тегов, а не между тегами (например, заменил на "1234").

Lorem ipsum 1234 dolor sit amet, consetetur sadipscing elitr, sed diam nonumy <a href="#" onclick="location.href='http://Test.com/url'; return false;">Test</a> eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd sed 1234 dolores et ea rebum. Stet clita kasd gubergren, no sea <a href="#" onclick="location.href='http://url.com'; return false;">Test xyz</a> takimata sanctus est Lorem ipsum dolor sit amet.

Я начал с этого регулярного выражения: (?!<a[^>]*>)(Test)([^<])(?!</a>)

Но две проблемы не решены:

  1. Текст "Тест" также заменяется внутри тегов (например, <a href="#" onclick="location.href='http://Test.com/url'; return false;">)
  2. Если текст между тегом не точно соответствует искомому тексту, он также будет заменен (например, <a href="#" onclick="location.href='http://url'; return false;">Test xyz</a>)

Я надеюсь, что у кого-то есть решение этой проблемы.

Ответ 1

(?!<a[^>]*?>)(Test)(?![^<]*?</a>)

то же, что и zb226, но оптимизировано с ленивым соответствием

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

Ответ 2

Ответ

использование

(Test)(?!(.(?!<a))*</a>)

объяснение

Позвольте мне напомнить вам о значении некоторых символов:

1) ?! является негативным взглядом, например, r(?!d) выбирает все r, за которыми непосредственно не следует d:

enter image description here

2) Поэтому никогда не начинайте отрицательный взгляд без персонажа. Просто (?!d) бессмысленно

enter image description here

3) Чем ? можно использовать как ленивый матч Например .+E будет выбирать из

123EEE

вся строка 123EEE. Тем не менее,. .+?E выбирает столько "любой характер" (.+), .+?E необходимо. Было бы только выбрать 123E.

Ответ:

Ответ простейшего заключается в том, что вы должны использовать (?!<a[^>]*?>)(Test)(?![^<]*?</a>) <A [^> (?!<a[^>]*?>)(Test)(?![^<]*?</a>). Позвольте мне сначала объяснить, как сделать это короче.

Как упомянуто в 2), бессмысленно смотреть в будущее перед матчем. Таким образом, следующее эквивалентно ответу простейшего:

(Test)(?![^<]*?</a>)

Кроме того, < не допускается, ленивый матч ? является излишним, так что его также эквивалентно

(Test)(?![^<]*</a>)

При этом выбираются все Test, за которыми не следует </a> без символа < между ними. Вот почему тест, который появляется до или после любого <a...>.. </a> будет заменен.

Тем не менее, обратите внимание, что

Lorem Test dolor <a href="http://Test.com/url">Test <strong>dolor</strong></a> eirmod

будет изменен на

Lorem 1234 dolor <a href="http://1234.com/url">1234 <strong>dolor</strong></a> eirmod 

Чтобы поймать это, вы можете изменить свое регулярное выражение на

(Test)(?!(.(?!<a))*</a>)

который делает следующее:

Выберите каждое слово Test, за которым не следует строка ***</a> где за каждым символом в *** не следует <a.

Обратите внимание, что точка . важно (см. 2)).

Обратите внимание, что ленивое совпадение типа (Test)(?!(.(?!<a))*?</a>) не имеет значения, потому что вложенные ссылки недопустимы в HTML4 и HTML5 (что-то вроде <a href="#">..<a href="#">...</a>..</a>).

протист сказал

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

Я согласен с этим. Проблема в том, что это может вызвать проблемы, если тег не закрыт или не открыт. Например, все упомянутые решения будут изменены

Lorem Test dolor Test <strong>dolor</strong></a> eirmod

в

Lorem Test dolor Test <strong>dolor</strong></a> eirmod 1234 dolores sea 1234 takimata 

Ответ 3

Это должно сделать трюк:

(<a[^>]*>)(Test)(?![^<]*</a>)

Попробуйте сами на regexr.

Ответ 4

Воскрешая этот древний вопрос, потому что у него было простое решение, о котором не упоминалось.

При всех отказах в использовании regex для синтаксического анализа html, это простой способ сделать это.

Метод Perl/PCRE

<a[^>]*>[^<]*<\/a(*SKIP)(*F)|Test

демо

Общее решение

<a[^>]*>[^<]*<\/a|(Test)

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

демо

Ссылка

Ответ 5

Адаптируем предлагаемое решение @protist, в этом случае ищем фразу и исключаем любые совпадения внутри тега скрипта:

(?!<script[^>]*?>)(\bTest Phrase\b)(?![^<]*?<\/script>)

демонстрация

Ответ, предоставленный Адамом, хотя и является более кратким, требует больше времени для выполнения. Это можно доказать, отредактировав демоверсию, уже упомянутую в этом комментарии.