Php 忽略preg_replace中的html标记

Php 忽略preg_replace中的html标记,php,html,preg-replace,Php,Html,Preg Replace,如何在本节中忽略html标记。 我有一个foreach函数用于搜索,因此如果有人搜索“apple span”,preg_replace也会将span应用于span,html会中断: preg_replace("/($keyword)/i","<span class=\"search_hightlight\">$1</span>",$str); preg_replace(“/($keyword)/i”、“$1”、$str); 提前谢谢 我认为您应该基于DOMDocume

如何在本节中忽略html标记。 我有一个foreach函数用于搜索,因此如果有人搜索“apple span”,preg_replace也会将span应用于span,html会中断:

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

提前谢谢

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

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

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

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

然后,您只需将这些文本包装到
中即可

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

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

'//*[contains(., "'.$search.'")]/*[FALSE = contains(., "'.$search.'")]/..'
$search
包含要搜索的文本,不包含任何
(引号)字符(如果需要引号,请参阅解决方法)

此查询将返回包含textnodes的所有父节点,这些节点组合在一起将是一个包含搜索词的字符串

由于这样的列表不容易进一步处理,我创建了一个
TextRange
类,它表示
DOMText
节点列表。在textnodes列表上执行字符串操作非常有用,就像它们是一个字符串一样

这是例程的基本框架:

$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>

可能重复-抱歉,我被卡住了,但现在我可以通过您的链接找到它:/($keyword)(?=[^>]*(+1感谢您使用久经考验的工具进行html解析/操作。谢谢您的解释。我真的很感激!如果您能提供一个示例,我会很高兴,但我也会亲自阅读DOMDocument和DOMPath。谢谢!@Fabian:我有一个示例正在我的机器上运行,但因为这个原因无法在在线代码板上运行我试图在另一个问题中解决这个问题,我刚刚准备发布:-如果我找到了问题的解决方案,我会在这里发布代码,这样它是正确的。到目前为止的代码在这里:-但是它不适用于
上的第一个文本,而它在我的开发框上工作。我必须使用以下代码XPath,否则它找不到没有子节点的匹配节点:“//*[contains(,“$search”)]/*[FALSE=contains(,“$search”)]/.|/*[contains(,,“$search”)和count(*)=0]”TextRange类位于:
<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>
 while(FALSE !== $start = mb_strpos($range, $search, 0, 'UTF-8'))