Я знаю, что регулярное выражение не идеально подходит для использования со строками 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>
Есть идеи?
Использование DOM, безусловно, было бы предпочтительнее.
Однако вам может быть так:
$result = preg_replace('%Amazon(?![^<]*</a>)%i', '<a href="http://www.amazon.com">Amazon</a>', $subject);
Он соответствует Amazon
только если
</a>
, <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 гарантирует отсутствие закрывающего тега. И я ищу там только для не <
так что я не буду читать открывающий тег случайно.
К сожалению, я думаю, что логика, в которой вы нуждаетесь, еще сложнее, чем сопоставление текстовых шаблонов: – /
Я знаю, что это не тот ответ, который вы хотите услышать, но вы, вероятно, получите лучшие результаты с помощью модели 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);