Регулярное выражение соответствует словам или фразам в строке, но НЕ совпадает с частью URL или внутри тегов <a> </a>. (РНР)

Я знаю, что регулярное выражение не идеально подходит для использования со строками HTML, и я просмотрел PHP Simple HTML DOM Parser, но все же считаю, что это путь. Все теги HTML будут сгенерированы моим программным обеспечением форума, чтобы они были согласованными и действительными HTML.

Я пытаюсь сделать плагин, который найдет список ключевых слов (или фраз) в строке HTML и заменит их ссылкой, которую я указываю. Например, если кто-то типа:

I use Amazon for that. 

он заменит его:

 I use <a href="http://www.amazon.com">Amazon</a> for that. 

Проблема, конечно же, в том, что если «амазонка» находится в URL-адресе, она также будет заменена. Я решил эту проблему с функцией обратного вызова, найденной на этом сайте, слегка измененной.

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

 <a href="http://www.amazon.com">My Amazon Link</a> 

Он будет соответствовать «Amazon» в «My Amazon Link»

Я действительно нуждаюсь в регулярном выражении, чтобы соответствовать «амазонке» где угодно, кроме <a href и </a>

Есть идеи?

Related of "Регулярное выражение соответствует словам или фразам в строке, но НЕ совпадает с частью URL или внутри тегов <a> </a>. (РНР)"

Использование DOM, безусловно, было бы предпочтительнее.

Однако вам может быть так:

 $result = preg_replace('%Amazon(?![^<]*</a>)%i', '<a href="http://www.amazon.com">Amazon</a>', $subject); 

Он соответствует Amazon только если

  1. за ним не следует закрывающий тег </a> ,
  2. он не является частью тега,
  3. нет промежуточных тегов, то есть они будут сброшены, если теги могут быть вложены внутри тегов <a> .

Поэтому это изменит это:

 I use Amazon for that. I use <a href="http://www.amazon.com">Amazon</a> for that. <a href="http://www.amazon.com">My Amazon Link</a> It will match the "Amazon" in "My Amazon Link" 

в это:

 I use <a href="http://www.amazon.com">Amazon</a> for that. I use <a href="http://www.amazon.com">Amazon</a> for that. <a href="http://www.amazon.com">My Amazon Link</a> It will match the "<a href="http://www.amazon.com">Amazon</a>" in "My <a href="http://www.amazon.com">Amazon</a> Link" 

Не делай этого. Вы не можете надежно сделать это с помощью Regex, независимо от того, насколько совместим ваш HTML.

Что-то вроде этого должно работать, однако:

 <?php $dom = new DOMDocument; $dom->load('test.xml'); $x = new DOMXPath($dom); $nodes = $x->query("//text()[contains(., 'Amazon')][not(ancestor::a)]"); foreach ($nodes as $node) { while (false !== strpos($node->nodeValue, 'Amazon')) { $word = $node->splitText(strpos($node->nodeValue, 'Amazon')); $after = $word->splitText(6); $link = $dom->createElement('a'); $link->setAttribute('href', 'http://www.amazon.com'); $word->parentNode->replaceChild($link, $word); $link->appendChild($word); $node = $after; } } $html = $dom->saveHTML(); echo $html; 

Это многословие, но это действительно сработает.

Попробуйте это здесь

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

Это приведет к поиску Amazon, и отрицательный lookahead гарантирует отсутствие закрывающего тега. И я ищу там только для не < так что я не буду читать открывающий тег случайно.

http://regexr.com

К сожалению, я думаю, что логика, в которой вы нуждаетесь, еще сложнее, чем сопоставление текстовых шаблонов: – /

Я знаю, что это не тот ответ, который вы хотите услышать, но вы, вероятно, получите лучшие результаты с помощью модели DOM.

Вот обсуждение этой темы в другом месте: http://coderzone.org/forum/index.php?topic=84.0

Можно ли просто запустить фильтр один раз, так что вы не закончите с обманами? Или исходный корпус также может содержать ссылки?

Джо, воскресив этот вопрос, потому что у него было простое решение, о котором не упоминалось. (Нашел ваш вопрос, проведя некоторое исследование для общего вопроса о том, как исключить шаблоны в регулярном выражении .)

Со всеми отказами об использовании regex для анализа html, вот простой способ сделать это.

Вот наше простое регулярное выражение:

 <a.*?</a>(*SKIP)(*F)|amazon 

Левая часть чередования соответствует полному <a... </a> a>, а затем сознательно терпит неудачу. Правая сторона соответствует amazon , и мы знаем, что это правильная amazon потому что она не соответствовала выражению слева.

Эта программа показывает, как использовать регулярное выражение (см. Результаты в нижней части онлайн-демонстрации ):

 <?php $target = "word1 <a stuff amazon> </a> word2 amazon"; $regex = "~(?i)<a.*?</a>(*SKIP)(*F)|amazon~"; $repl= '<a href="http://www.amazon.com">Amazon</a>'; $new=preg_replace($regex,$repl,$target); echo htmlentities($new); 

Справка

Как сопоставить (или заменить) шаблон, за исключением ситуаций s1, s2, s3 …

Используйте этот код:

 $p = '~((<a\s)(?(2)[^>]*?>))?(amazon)~smi'; $str = '<a href="http://www.amazon.com">Amazon</a>'; $s = preg_replace($p, "$1My $3 Link", $str); var_dump($s); 

ВЫВОД

 String(50) "<a href="http://www.amazon.com">My Amazon Link</a>" 

Импровизация. Он должен связываться только в том случае, если это целое слово «Amazon», а не такие слова, как AmazonWorld.

 $result = preg_replace('%\bAmazon(?![^<]*</a>)\b%i', '<a href="http://www.amazon.com">Amazon</a>', $subject);