通过跳过锚标记进行正则表达式检查


Regular expression check by skipping anchor tags

我已经写了一个搜索特定关键字的正则表达式,我正在用特定的URL替换该关键字。

我当前的正则表达式是:'b$keyword'b

这里的一个问题是,如果我的数据包含锚标记,并且该标记包含此关键字,则此正则表达式也将替换锚标记中的该关键字。

我想在给定的数据中搜索,不包括锚标记。请帮帮我。谢谢你的帮助。

。关键字:迪斯尼

我/p

:

This is <a href="/test.php"> Disney </a> The disney should be replaceable
预期O/p:

This is <a href="/test.php"> Disney </a> The <a href="any-url.php">disney</a> should be replaceable
无效o/p:

This is <a href="/test.php"> <a href="any-url.php">Disney</a> </a> The <a href="any-url.php">disney</a> should be replaceable

我已经修改了在页面上突出显示搜索短语的功能,现在开始:

$html = 'This is <a href="/test.php"> Disney </a> The disney should be replaceable.'.PHP_EOL;
$html .= 'Let''s test also use of keyword inside other tags, for example as class name:'.PHP_EOL;
$html .= '<b class=disney></b> - this should not be replaced with link, and it isn''t!'.PHP_EOL;
$result = ReplaceKeywordWithLink($html, "disney", "any-url.php");
echo nl2br(htmlspecialchars($result));
function ReplaceKeywordWithLink($html, $keyword, $link)
{
    if (strpos($html, "<") !== false) {
        $id = 0;
        $unique_array = array();
        // Hide existing anchor tags with some unique string.
        preg_match_all("#<a[^<>]*>['s'S]*?</a>#i", $html, $matches);
        foreach ($matches[0] as $tag) {
            $id++;
            $unique_string = "@@@@@$id@@@@@";
            $unique_array[$unique_string] = $tag;
            $html = str_replace($tag, $unique_string, $html);
        }
        // Hide all tags by replacing with some unique string.
        preg_match_all("#<[^<>]+>#", $html, $matches);      
        foreach ($matches[0] as $tag) {
            $id++;
            $unique_string = "@@@@@$id@@@@@";
            $unique_array[$unique_string] = $tag;
            $html = str_replace($tag, $unique_string, $html);
        }
    }
    // Then we replace the keyword with link.
    $keyword = preg_quote($keyword);
    assert(strpos($keyword, '$') === false);
    $html = preg_replace('#('b)('.$keyword.')('b)#i', '$1<a href="'.$link.'">$2</a>$3', $html);
    // We get back all the tags by replacing unique strings with their corresponding tag.
    if (isset($unique_array)) {     
        foreach ($unique_array as $unique_string => $tag) {
            $html = str_replace($unique_string, $tag, $html);
        }
    }
    return $html;
}
结果:

This is <a href="/test.php"> Disney </a> The <a href="any-url.php">disney</a> should be replaceable.
Let's test also use of keyword inside other tags, for example as class name:
<b class=disney></b> - this should not be replaced with link, and it isn't!

将此添加到正则表达式末尾:

(?=[^<]*(?:<(?!/?a'b)[^<]*)*(?:<a'b|'z))

这个前瞻性尝试匹配下一个开始的<a>标签或输入的结束,但只有当它没有看到一个结束的</a>标签。假设HTML是最低限度的格式良好的,那么只要匹配开始于<a>标记开始之后和相应的</a>标记之前,就会失败。

为了防止它在任何其他标签(例如<div class="disney">)内匹配,你也可以添加这个向前看:

(?![^<>]*+>)

我假设在标签的属性值中不会有任何尖括号,根据HTML 4规范,这是合法的,但在现实世界中非常罕见。

如果您正在以PHP双引号字符串的形式编写正则表达式(如果您希望替换$keyword变量,则必须是这样),您应该将所有反斜杠都加双。'z可能不会是一个问题,但我相信'b会被解释为退格,而不是作为一个词边界断言。

EDIT:再想一下,肯定要添加第二个向前看-我的意思是,为什么想要阻止标签内的匹配?把它放在第一位,因为它的计算速度会比另一个更快:

(?![^<>]*+>)(?=[^<]*(?:<(?!/?a'b)[^<]*)*(?:<a'b|'z))

先剥离标签,然后在剥离后的文本上搜索。