我知道正则表达式不适合与HTML字符串一起使用,我已经看过PHP简单HTML DOM解析器,但仍然相信这是要走的路。所有 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>
它将匹配"我的亚马逊链接"中的"亚马逊"
我真正需要的是一个正则表达式来匹配除 <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"
不要这样做。你不能用正则表达式可靠地做到这一点,无论你的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>)
这将搜索亚马逊,负面的展望确保后面没有关闭标签。我在那里搜索只是为了不<
这样我就不会意外地阅读开始标签。
http://regexr.com
我认为您需要的逻辑仍然比文本模式匹配更复杂:-/
我知道这不是你想听到的答案,但使用 DOM 模型你可能会得到更好的结果。
以下是其他地方对这个话题的讨论:http://coderzone.org/forum/index.php?topic=84.0
是否可以只运行一次过滤器,这样您就不会被欺骗?或者原始语料库也可以包含链接吗?
Joe,复活这个问题,因为它有一个简单的解决方案,没有提到。(在对如何在正则表达式中排除模式的一般问题进行一些研究时发现了您的问题。
有了关于使用正则表达式解析 html 的所有免责声明,这里有一种简单的方法。
这是我们的简单正则表达式:
<a.*?</a>(*SKIP)(*F)|amazon
交替的左侧匹配完整的<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);