簡體   English   中英

突出顯示段落中的關鍵字

[英]Highlight keywords in a paragraph

我需要在一個段落中突出顯示關鍵字,就像Google在搜索結果中所做的那樣。 我們假設我有一個帶有博客文章的MySQL數據庫。 當用戶搜索某個關鍵字時,我希望返回包含這些關鍵字的帖子,但只顯示部分帖子(包含搜索關鍵字的段落)並突出顯示這些關鍵字。

我的計划是這樣的:

  • 找到在其內容中包含搜索關鍵字的帖子ID;
  • 再次讀取該帖子的內容,並將每個單詞放在一個固定的緩沖區數組(50個單詞)中,直到找到該關鍵字。

你能幫助我一些邏輯,或者至少告訴我我的邏輯是否合適? 我正處於PHP學習階段。

如果它包含html(請注意,這是一個非常強大的解決方案):

$string = '<p>foo<b>bar</b></p>';
$keyword = 'foo';
$dom = new DomDocument();
$dom->loadHtml($string);
$xpath = new DomXpath($dom);
$elements = $xpath->query('//*[contains(.,"'.$keyword.'")]');
foreach ($elements as $element) {
    foreach ($element->childNodes as $child) {
        if (!$child instanceof DomText) continue;
        $fragment = $dom->createDocumentFragment();
        $text = $child->textContent;
        $stubs = array();
        while (($pos = stripos($text, $keyword)) !== false) {
            $fragment->appendChild(new DomText(substr($text, 0, $pos)));
            $word = substr($text, $pos, strlen($keyword));
            $highlight = $dom->createElement('span');
            $highlight->appendChild(new DomText($word));
            $highlight->setAttribute('class', 'highlight');
            $fragment->appendChild($highlight);
            $text = substr($text, $pos + strlen($keyword));
        }
        if (!empty($text)) $fragment->appendChild(new DomText($text));
        $element->replaceChild($fragment, $child);
    }
}
$string = $dom->saveXml($dom->getElementsByTagName('body')->item(0)->firstChild);

結果是:

<p><span class="highlight">foo</span><b>bar</b></p>

與:

$string = '<body><p>foobarbaz<b>bar</b></p></body>';
$keyword = 'bar';

你得到(分為多行以便於閱讀):

<p>foo
    <span class="highlight">bar</span>
    baz
    <b>
        <span class="highlight">bar</span>
    </b>
</p>

謹防非dom解決方案(如regexstr_replace ),因為突出顯示像“div”這樣的東西有完全破壞你的HTML的傾向......這只會“強調”正文中的字符串,永遠不會在標記內部......


編輯因為您需要Google樣式結果,所以這是一種方法:

function getKeywordStubs($string, array $keywords, $maxStubSize = 10) {
    $dom = new DomDocument();
    $dom->loadHtml($string);
    $xpath = new DomXpath($dom);
    $results = array();
    $maxStubHalf = ceil($maxStubSize / 2);
    foreach ($keywords as $keyword) {
        $elements = $xpath->query('//*[contains(.,"'.$keyword.'")]');
        $replace = '<span class="highlight">'.$keyword.'</span>';
        foreach ($elements as $element) {
            $stub = $element->textContent;
            $regex = '#^.*?((\w*\W*){'.
                 $maxStubHalf.'})('.
                 preg_quote($keyword, '#').
                 ')((\w*\W*){'.
                 $maxStubHalf.'}).*?$#ims';
            preg_match($regex, $stub, $match);
            var_dump($regex, $match);
            $stub = preg_replace($regex, '\\1\\3\\4', $stub);
            $stub = str_ireplace($keyword, $replace, $stub);
            $results[] = $stub;
        }
    }
    $results = array_unique($results);
    return $results;
}

好的,那么它的作用是返回一個帶有$maxStubSize的匹配數組(即之前的數字的一半,之后的一半)......

所以,給定一個字符串:

<p>a whole 
    <b>bunch of</b> text 
    <a>here for</a> 
    us to foo bar baz replace out from this string
    <b>bar</b>
</p>

調用getKeywordStubs($string, array('bar', 'bunch'))將導致:

array(4) {
  [0]=>
  string(75) "here for us to foo <span class="highlight">bar</span> baz replace out from "
  [3]=>
  string(34) "<span class="highlight">bar</span>"
  [4]=>
  string(62) "a whole <span class="highlight">bunch</span> of text here for "
  [7]=>
  string(39) "<span class="highlight">bunch</span> of"
}

那么,你可以通過strlen對列表進行排序然后挑選兩個最長的匹配來構建你的結果模糊...(假設php 5.3+):

usort($results, function($str1, $str2) { 
    return strlen($str2) - strlen($str1);
});
$description = implode('...', array_slice($results, 0, 2));

結果如下:

here for us to foo <span class="highlight">bar</span> baz replace out...a whole <span class="highlight">bunch</span> of text here for 

我希望有所幫助...(我覺得這有點......臃腫......我確信有更好的方法可以做到這一點,但這是一種方式)......

當你連接到數據庫時,也許你可以這樣做:

$keyword = $_REQUEST["keyword"]; //fetch the keyword from the request
$result = mysql_query("SELECT * FROM `posts` WHERE `content` LIKE '%".
        mysql_real_escape_string($keyword)."%'"); //ask the database for the posttexts
while ($row = mysql_fetch_array($result)) {//do the following for each result:
  $text = $row["content"];//we're only interested in the content at the moment
  $text=substr ($text, strrpos($text, $keyword)-150, 300); //cut out
  $text=str_replace($keyword, '<strong>'.$keyword.'</strong>', $text); //highlight
  echo htmlentities($text); //print it
  echo "<hr>";//draw a line under it
}

如果你想刪除相關的段落,在完成上面提到的str_replace函數之后,你可以使用stripos()來找到這些強段的位置,並使用substr()的那個位置的偏移來切出一段段落,例如:

$searchterms;

foreach($searchterms as $search)
{
$paragraph = str_replace($search, "<strong>$search</strong>", $paragraph);
}

$pos = 0;

for($i = 0; $i < 4; $i++)  
{  
$pos = stripos($paragraph, "<strong>", $pos);  
$section[$i] = substr($paragraph, $pos - 100, 200);
}

它將為您提供一系列小句子(每個200個字符),以便您按照自己的意願使用。 從切割位置搜索最近的空間並從那里切割以防止半字也可能是有益的。 哦,你還需要檢查錯誤,但我會離開,但由你決定。

我在搜索如何突出顯示關鍵字搜索結果時發現了這篇文章。 我的要求是:

  • 必須是整個詞
  • 必須適用於多個關鍵字
  • 必須只是PHP

我通過設計存儲數據的表單從MySQL數據庫中獲取數據,該數據庫不包含元素。

這是我發現最有用的代碼:

$keywords = array("fox","jump","quick");
$string = "The quick brown fox jumps over the lazy dog";
$test = "The quick brown fox jumps over the lazy dog"; // used to compare values at the end.

if(isset($keywords)) // For keyword search this will highlight all keywords in the results.
    {
    foreach($keywords as $word)
        {
        $pattern = "/\b".$word."\b/i";
        $string = preg_replace($pattern,"<span class=\"highlight\">".$word."</span>", $string);
        }
    }
 // We must compare the original string to the string altered in the loop to avoid having a string printed with no matches.
if($string === $test)
    {
    echo "No match";
    }
else
    {
    echo $string;
    }

輸出:

The <span class="highlight">quick</span> brown <span class="highlight">fox</span> jumps over the lazy dog.

我希望這可以幫助別人。

您可以嘗試使用explode將數據庫搜索結果集explode成數組,然后在每個搜索結果上使用array_search() 在下面的示例中將$distance變量設置為您希望在$keyword的第一個匹配項的任一側顯示的單詞數。

在示例中,我將lorum ipsum文本作為示例數據庫結果段落並將$keyword設置為'scelerisque'。 您顯然會在代碼中替換它們。

//example paragraph text
$lorum = 'Nunc nec magna at nibh imperdiet dignissim quis eu velit. 
vel mattis odio rutrum nec. Etiam sit amet tortor nibh, molestie 
vestibulum tortor. Integer condimentum magna dictum purus vehicula 
et scelerisque mauris viverra. Nullam in lorem erat. Ut dolor libero, 
tristique et pellentesque sed, mattis eget dui. Cum sociis natoque 
penatibus et magnis dis parturient montes, nascetur ridiculus mus. 
.';

//turn paragraph into array
$ipsum = explode(' ',$lorum);
//set keyword
$keyword = 'scelerisque';
//set excerpt distance
$distance = 10;

//look for keyword in paragraph array, return array key of first match
$match_key = array_search($keyword,$ipsum);

if(!empty($match_key)){

    foreach($ipsum as $key=>$value){
        //if paragraph array key inside excerpt distance
        if($key > $match_key-$distance and $key< $match_key+$distance){ 
            //if array key matches keyword key, bold the word
            if($key == $match_key){
                $word = '<b>'.$value.'</b>';
                }
            else{
                $word = $value;
                }
            //create excerpt array to hold words within distance
            $excerpt[] = $word;
            }

        }
    //turn excerpt array into a string
    $excerpt = implode(' ',$excerpt);
    }
//print the string
echo $excerpt;

$excerpt返回:“vestibulum tortor.Integer condimentum magna dictum purus vehicula et scelerisque mauris viverra.Nullam in lorem erat.Ut dolor libero,”

這是純文本的解決方案:

$str = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
$keywords = array('co');
$wordspan = 5;
$keywordsPattern = implode('|', array_map(function($val) { return preg_quote($val, '/'); }, $keywords));
$matches = preg_split("/($keywordsPattern)/ui", $str, -1, PREG_SPLIT_DELIM_CAPTURE);
for ($i = 0, $n = count($matches); $i < $n; ++$i) {
    if ($i % 2 == 0) {
        $words = preg_split('/(\s+)/u', $matches[$i], -1, PREG_SPLIT_DELIM_CAPTURE);
        if (count($words) > ($wordspan+1)*2) {
            $matches[$i] = '…';
            if ($i > 0) {
                $matches[$i] = implode('', array_slice($words, 0, ($wordspan+1)*2)) . $matches[$i];
            }
            if ($i < $n-1) {
                $matches[$i] .= implode('', array_slice($words, -($wordspan+1)*2));
            }
        }
    } else {
        $matches[$i] = '<b>'.$matches[$i].'</b>';
    }
}
echo implode('', $matches);

使用當前模式"/($keywordsPattern)/ui"匹配並突出顯示子字。 但是如果你想要改變它,你可以:

  • 如果您只想匹配整個單詞而不僅僅是子詞,請使用單詞邊界\\b

     "/\\b($keywordsPattern)\\b/ui" 
  • 如果您想匹配子詞但突出顯示整個單詞,請在關鍵字前面和后面使用put可選單詞字符\\w

     "/(\\w*?(?:$keywordsPattern)\\w*)/ui" 

如果你是一個初學者,這不會像有人想的那樣超級簡單......

我認為你應該做以下的步驟:

  1. 根據用戶搜索的內容構建查詢(謹防sql注入)
  2. 獲取結果並組織它們(數組應該沒問題)
  3. 從前一個數組構建html代碼

在第三步中,您可以使用一些正則表達式將用戶搜索的關鍵字替換為粗體等效項。 str_replace也可以工作......

我希望這有幫助...如果你可以提供你的數據庫結構,也許我可以給你一些更精確的提示......

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM