PHP搜索文本突出显示功能

时间:2011-12-19 16:48:36

标签: php highlight

我有一个PHP突出显示功能,可以使某些单词加粗。

下面是函数,它很有用,除非数组:$ words包含单个值:b

例如有人搜索:jessie j price tag feat b o b

这将在数组$ words中包含以下条目:jessie,j,price,tag,feat,b,o,b

当'b'出现时,我的整个功能出错了,它显示了一大堆错误的html标签。当然,我可以从数组中删除任何“b”值,但这并不理想,因为突出显示在某些查询中不起作用。

此示例脚本:

    function highlightWords2($text, $words)
    {
        $text =  ($text);
        foreach ($words as $word)
        {       
            $word = preg_quote($word);

            $text = preg_replace("/\b($word)\b/i", '<b>$1</b>', $text);

        }
        return $text;
    }


$string = 'jessie j price tag feat b o b';

$words = array('jessie','tag','b','o','b');

echo highlightWords2($string, $words);

将输出:

<<<b>b</b>><b>b</b></<b>b</b>>>jessie</<<b>b</b>><b>b</b></<b>b</b>>> j price <<<b>b</b>><b>b</b></<b>b</b>>>tag</<<b>b</b>><b>b</b></<b>b</b>>> feat <<b>b</b>><b>b</b></<b>b</b>> <<b>b</b>>o</<b>b</b>> <<b>b</b>><b>b</b></<b>b</b>>

这只会发生,因为数组中有“b”。

你能看到我可以改变的任何东西让它正常工作吗?

3 个答案:

答案 0 :(得分:5)

你的问题是,当你的功能通过并查找所有b为粗体时,它会看到粗体标签,并尝试加粗它们。

@symcbean已经接近但忘了一件事。

$string = 'jessie j price tag feat b o b';
$words = array('jessie','tag','b','o','b');

print hl($string, $words);

function hl($inp, $words)
{
  $replace=array_flip(array_flip($words)); // remove duplicates
  $pattern=array();
  foreach ($replace as $k=>$fword) {
     $pattern[]='/\b(' . $fword . ')(?!>)\b/i';
     $replace[$k]='<b>$1</b>';
  }
  return preg_replace($pattern, $replace, $inp);
}

你是否看到这添加了“(?!&gt;)”这是一个负面的预测断言,基本上它表示如果字符串后面没有“&gt;”则匹配可以看到的是打开粗体和关闭粗体标签。注意我只检查“&gt;”在字符串之后为了排除开始和结束粗体标记,因为在字符串的开头查找它不会捕获关闭粗体标记。上面的代码完全符合预期。

答案 1 :(得分:2)

您的基本问题是您非常频繁地替换HTML中的纯文本字符串。当你替换标签和属性中的文本时,这确实会导致小字符串的问题。

相反,您需要应用搜索并仅替换HTML文本之间的文本。此外,您也不想在另一个高亮显示内部突出显示。

要做这些事情,正则表达式非常有限。而是在PHP中使用HTML解析器,例如DOMDocument。使用HTML解析器,可以仅在HTML文本元素内部进行搜索(而不是在标签,属性和注释等其他内容)。

您可以在a previous answer of mine中找到文字的荧光笔,并详细说明其工作原理。问题是 Ignore html tags in preg_replace ,它与您的问题非常相似,因此可能此代码段很有用,它使用<span>代替<b>代码:

$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);
        }
    }
}

如果你将它用于多个搜索词,我会根据搜索词添加一个带有数字的附加类,这样你就可以用不同颜色的CSS很好地设置它的样式。

此外,您应删除重复的搜索词并使xpath表达式识别为不查找已经指定了高亮区域的元素的文本。

答案 2 :(得分:0)

如果是我,我会使用javascript。

但是使用PHP,因为问题似乎只是搜索中的重复条目,只需删除它们,也可以只运行一次preg_replace而不是多次....

$string = 'jessie j price tag feat b o b';
$words = array('jessie','tag','b','o','b');

print hl($string, $words);

function hl($inp, $words)
{
  $replace=array_flip(array_flip($words)); // remove duplicates
  $pattern=array();
  foreach ($replace as $k=>$fword) {
     $pattern[]='/\b(' . $fword . ')\b/i';
     $replace[$k]='<b>$1<b>';
  }
  return preg_replace($pattern, $replace, $inp);
}