【问题标题】:PHP Search Text Highlight FunctionPHP 搜索文本高亮功能
【发布时间】:2012-01-23 18:35:56
【问题描述】:

我有一个 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”。

你们能看到我可以改变什么以使其正常工作吗?

【问题讨论】:

  • 你在哪里找到这个函数的?
  • 我在网上找到了它,但实际上我刚刚解决了我的问题。如果我将 更改为 ,那么它可以完美运行。 preg_replace 中的 \b 一定是在使用 标签。
  • 这是一种解决方法,直到 strong 成为搜索词。
  • &lt;i&gt;carport&lt;/i&gt;&lt;i&gt;car&lt;/i&gt;port 中是否也需要突出显示 carport 之类的词?

标签: php highlight


【解决方案1】:

如果是我,我会使用 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);
}

【讨论】:

    【解决方案2】:

    您的问题是,当您的函数通过并查找所有 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);
    }
    

    你看到这个添加的“(?!>)”是一个否定的前瞻断言,基本上它说只有当字符串后面没有“>”时才匹配,这就是打开粗体和关闭粗体标签。请注意,我只在字符串之后检查“>”,以便同时排除开始和结束粗体标记,因为在字符串开头查找它不会捕获结束粗体标记。上面的代码完全符合预期。

    【讨论】:

    • 正要发布同样的内容。我还建议将 preg_replace 与数组一起使用,因为这将文本的通读次数限制为一次,从而消除了复杂性并提高了速度。
    • 如果文本具有包含搜索词的 HTML 属性怎么办?还是 HTML cmets?还是javascript?
    • @hakre 是的,如果你有 Blah 它会导致问题 废话。除了在通过突出显示功能运行之前清理所有 html 的输入之外,我不是 100% 确定如何解决这个问题。
    • 非常感谢@JoshStrange 先生……这为我节省了时间! :)
    • 如果在 HTML 标签中没有 @JoshStrange 对文本所说的内容,您可以正确使用 strip_tags() 仅与正则表达式匹配。我也遇到了搜索词中的实体(应该用 CSS 类突出显示)的问题,并想出了类似:$decoded = html_entity_decode($words, ENT_COMPAT, 'UTF-8');
    【解决方案3】:

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

    相反,您只需要对 HTML 文本之间的文本应用搜索和替换。此外,您也不想在另一个突出显示中突出显示。

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

    您可以在a previous answer of mine 中找到一个文本荧光笔,并详细描述了它的工作原理。问题是 Ignore html tags in preg_replace,它与您的问题非常相似,所以这个 sn-p 可能很有帮助,它使用 &lt;span&gt; 而不是 &lt;b&gt; 标签:

    $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 表达式意识到不查找已经是分配了高亮范围的元素的一部分的文本。

    【讨论】:

      猜你喜欢
      • 2017-05-26
      • 2015-08-28
      • 2013-05-07
      • 2012-01-21
      • 2012-11-27
      • 1970-01-01
      • 1970-01-01
      • 2017-07-16
      • 1970-01-01
      相关资源
      最近更新 更多