Twitter正则表达式只有在尚未链接时

时间:2014-10-23 16:19:28

标签: php regex

我知道这已经完成了死亡。我已经找到了很多关于这个主题的主题,并且已经提出了很多建议。但是,如果我有以下字符串:

@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。

由于

3 个答案:

答案 0 :(得分:2)

您可以有效地使用(*SKIP)(*FAIL)回溯控制动词。

~<a[^<]*</a>(*SKIP)(*F)|@(\w+)~

我们的想法是跳过位于<a ..标记之间的所有内容。在交替运算符的左侧,我们匹配我们不想要的子模式,使其失败并强制正则表达式引擎不重试子字符串。

Live Demo

答案 1 :(得分:1)

您需要在.*?之前在该负面预测中添加<\/a>。这样它就不会匹配已锚定的@个字符串。

(?<=^|(?<=[^a-zA-Z0-9-_\.]))@([A-Za-z0-9_]+)(?!.*?<\/a>)

DEMO

答案 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