我知道这已经完成了死亡。我已经找到了很多关于这个主题的主题,并且已经提出了很多建议。但是,如果我有以下字符串:
@testaccount
<a href="http://twitter.com/testaccount">@testaccount</a>
显然,我不想将第二个转换为链接,因为它已经是一个链接。我已成功找到第一个没有电子邮件的人(感谢此处已有几个问题)。
这是我已经拥有的模式:
/(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z]+[A-Za-z0-9_]+)/
这将完美地转换第一个,但第二个显然将成为一个双重链接&#39;。
所以我设法解决了我应该使用像(?!<\/a>)
这样的东西。但是,这只会删除t
的最后一个testaccount
。
基本上,我需要找到一种方法来忽略整个匹配,而不是只删除一个字符。这可能吗?
我使用的语言是PHP。
由于
答案 0 :(得分:2)
您可以有效地使用(*SKIP)
和(*FAIL)
回溯控制动词。
~<a[^<]*</a>(*SKIP)(*F)|@(\w+)~
我们的想法是跳过位于<a ..
标记之间的所有内容。在交替运算符的左侧,我们匹配我们不想要的子模式,使其失败并强制正则表达式引擎不重试子字符串。
答案 1 :(得分:1)
您需要在.*?
之前在该负面预测中添加<\/a>
。这样它就不会匹配已锚定的@
个字符串。
(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z0-9_]+)(?!.*?<\/a>)
答案 2 :(得分:0)
正则表达式,不好。解析,很好。
$dom = new DOMDocument();
$dom->loadHTML("<div>".$your_html_source_here."</div>",
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$xpath = new DOMXPath($dom);
$nodes = $xpath->query("//text()[contains(.,'@')][not(ancestor::a)]");
foreach($nodes as $node) {
// each of these nodes contains at least one @ to be processed
// note that children of <a> tags are automatically ignored
preg_match_all("/(?:^|(?<=\s))@\w+/",$node->nodeValue,$matches,
PREG_PATTERN_ORDER|PREG_OFFSET_CAPTURE);
// work backwards - it's easier
foreach(array_reverse($matches[0]) as $match) {
list($text,$offset) = $match;
$node->splitText($offset+mb_strlen($text));
$middle = $node->splitText($offset);
// now wrap the text in a link:
$link = $dom->createElement('a');
$link->setAttribute("href","http://twitter.com/".substr($text,1));
$node->parentNode->insertBefore($link,$middle);
$link->appendChild($middle);
}
}
// output
$result = substr(trim($dom->saveHTML()),strlen("<div>"),-strlen("</div>"));
(注意:在内容周围添加<div>
是为了确保存在根元素 - 否则解析会遇到问题。)
示范here