php - 忽略 preg_replace 中的 html 标签

标签 php html preg-replace

如何在此 preg_replace 中忽略 html 标签。 我有一个用于搜索的 foreach 函数,所以如果有人搜索“apple span”,preg_replace 也会对 span 和 html 中断应用一个 span:

preg_replace("/($keyword)/i","<span class=\"search_hightlight\">$1</span>",$str);

提前致谢!

最佳答案

我假设您应该基于 DOMDocument 和 DOMXPath 创建您的函数,而不是使用正则表达式。即使这些功能非常强大,您也会遇到您所描述的问题,这些问题并不(总是)容易且稳健地用正则表达式解决。

一般的说法是:不要用正则表达式解析 HTML。

这是一个值得牢记的好规则,尽管与任何规则一样,它并不总是适用,但值得您下定决心。

XPath 允许您仅在文本中查找包含搜索词的所有文本,而忽略所有 XML 元素。

然后您只需将这些文本包装到 <span> 中即可。

编辑: 最后是一些代码;)

首先,它使用 xpath 来定位包含搜索文本的元素。我的查询看起来像这样,这可能写得更好,我不是 super xpath 专家:

'//*[contains(., "'.$search.'")]/*[FALSE = contains(., "'.$search.'")]/..'

$search 包含要搜索的文本, 包含任何 "(引号)字符(这会破坏它,如果您需要引号,请参阅 Cleaning/sanitizing xpath attributes 以获得解决方法)。

此查询将返回所有包含文本节点的父项,这些文本节点放在一起将是一个包含您的搜索词的字符串。

由于这样的列表不容易按原样进一步处理,我创建了一个表示 TextRange 节点列表的 DOMText 类。将文本节点列表作为一个字符串进行字符串操作很有用。

这是例程的基本框架:

$str = '...'; # some XML

$search = 'text that span';

printf("Searching for: (%d) '%s'\n", strlen($search), $search);

$doc = new DOMDocument;
$doc->loadXML($str);
$xp = new DOMXPath($doc);

$anchor = $doc->getElementsByTagName('body')->item(0);
if (!$anchor)
{
    throw new Exception('Anchor element not found.');
}

// search elements that contain the search-text
$r = $xp->query('//*[contains(., "'.$search.'")]/*[FALSE = contains(., "'.$search.'")]/..', $anchor);
if (!$r)
{
    throw new Exception('XPath failed.');
}

// process search results
foreach($r as $i => $node)
{   
    $textNodes = $xp->query('.//child::text()', $node);

    // extract $search textnode ranges, create fitting nodes if necessary
    $range = new TextRange($textNodes);        
    $ranges = array();
    while(FALSE !== $start = strpos($range, $search))
    {
        $base = $range->split($start);
        $range = $base->split(strlen($search));
        $ranges[] = $base;
    };

    // wrap every each matching textnode
    foreach($ranges as $range)
    {
        foreach($range->getNodes() as $node)
        {
            $span = $doc->createElement('span');
            $span->setAttribute('class', 'search_hightlight');
            $node = $node->parentNode->replaceChild($span, $node);
            $span->appendChild($node);
        }
    }
}

对于我的示例 XML:

<html>
    <body>
        This is some <span>text</span> that span across a page to search in.
    and more text that span</body>
</html>

它产生以下结果:

<html>
    <body>
        This is some <span><span class="search_hightlight">text</span></span><span class="search_hightlight"> that span</span> across a page to search in.
    and more <span class="search_hightlight">text that span</span></body>
</html>

这表明这甚至允许查找分布在多个标签中的文本。使用正则表达式根本就没那么容易。

您可以在此处找到完整代码:http://codepad.viper-7.com/U4bxbe(包括我从答案示例中取出的 TextRange 类)。

由于该站点使用的是较旧的 LIBXML 版本,它在 viper 键盘上无法正常工作。它适用于我的 LIBXML 版本 20707。我创建了一个关于此问题的相关问题:XPath query result order

注意事项:此示例使用二进制字符串搜索 ( strpos ) 和相关偏移量来使用 DOMText::splitText 函数拆分文本节点。这可能导致错误的偏移量,因为函数需要 UTF-8 字符偏移量。正确的方法是使用mb_strpos获取基于UTF-8的值。

该示例无论如何都有效,因为它仅使用 US-ASCII,它与示例数据的 UTF-8 具有相同的偏移量。

在现实生活中,$search 字符串应该是 UTF-8 编码的,并且应该使用 mb_strpos 而不是 strpos :

 while(FALSE !== $start = mb_strpos($range, $search, 0, 'UTF-8'))

关于php - 忽略 preg_replace 中的 html 标签,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8193327/

相关文章:

php - 为什么 mysqli 给出 "Commands out of sync"错误?

php - PHP 脚本中的函数导致 502 Bad Gateway 错误

html - 框在文本前面

javascript - 使用正则表达式对垃圾邮件机器人隐藏电子邮件地址

php - 替换 http 和 https..?

javascript - 比较 MySQL 条目中的 'closest' 匹配

php - 自定义 MySQL 和 PHP 论坛 - 在类别下列出主题

html - 根据使用字体堆栈中的字体调整字体大小

javascript - 使用 jquery 为动态创建的 div 指定 <h4 id ="name"> 标签

javascript - 如何使用正则表达式获取标签内容?