2011-06-29 150 views
22

我想要做的是做一個'專業術語'。 基本上我在數據庫中有一些html和一些術語表。 當用戶點擊專業術語時,它會用一個漂亮的工具提示(wztooltip)替換文本中的單詞,以顯示它們的含義。在HTML中搜索和替換單詞

我已經就這一個努力,在這個問題 Regex/DOMDocument - match and replace text not in a link

一直在尋找嚴重,它似乎是答案在於simple_html_dom庫,但我無法得到它的工作。 顯然,任何已經鏈接的單詞都不會被觸及。 這是我得到的東西。

$html = str_get_html($article['content']); 

$query_glossary = "SELECT word,glossary_term_id,info FROM glossary_terms WHERE status = 1 ORDER BY LENGTH(word) DESC"; 
$result_glossary = mysql_query_run($query_glossary); 

while($glossary = mysql_fetch_array($result_glossary)) { 
    $glossary_link = SITEURL.'/glossary/term/'.string_to_url($glossary['word']).'-'.$glossary['glossary_term_id']; 
    if(strlen($glossary['info'])>400) { 
     $glossary_info = substr(strip_tags($glossary['info']),0,350).' ...<br /> <a href="'.$glossary_link.'">Read More</a>'; 
    } 
    else { 
     $glossary_info = $glossary['info']; 
    } 
    $glossary_tip = 'href="javascript:;" onmouseout="UnTip();" class="article_jargon_highligher" onmouseover="'.tooltip_javascript('<a href="'.$glossary_link.'">'.$glossary['word'].'</a>',$glossary_info,400,1,0,1).'"'; 
    $glossary_word = $glossary['word']; 
    $glossary_word = preg_quote($glossary_word,'/'); 

    //once done we can replace the words with a nice tip  
    foreach ($html->find('text') as $element) { 
     if (!in_array($element->parent()->tag,array())) { 
      //problems are case aren't taken into account and grammer 
      $element->innertext = str_ireplace(''.$glossary['word'].' ',' <a '.$glossary_tip.' >'.$glossary['word'].'</a> ', $element->innertext); 

      //$element->innertext = str_ireplace(''.$glossary['word'].',',' <a '.$glossary_tip.'>'.$glossary['word'].'</a> ', $element->innertext); 
      //$element->innertext = preg_replace ("/\s(".$glossary_word.")\s/ise","nothing(' <a'.'$glossary_tip.'>'.'$1'.'</a> ')" , $element->innertext); 
      // $element->innertext = str_replace('__glossary_tip_replace__',$glossary_tip, $element->innertext); 
     } 
    } 
} 
$article['content'] = $html->save(); 
+0

我是一個同事。真正的問題是,我們無法讓代碼只匹配不好的單詞,而不是單詞中的單詞(也許是APS)。這些詞也在HTML中。所以需要考慮。 – David

+0

這當然只是寫一個足夠強大的正則表達式,可能使用空白和標點符號來檢測單詞邊界,儘管我不會試圖讓自己難堪。+1 – shanethehat

+0

您是否想要JS解決方案或PHP解決方案,因爲您使用了兩個標籤? – Gerben

回答

11

使用反相字字符\W選擇比你的正則表達式的數字和字母以外的任何字符。因爲這仍然會在文本塊的邊界上失敗,所以還需要測試這些條件。因此,使用單詞「術語」作爲文本您正在搜索:

(^term$)|(^term\W)|(\Wterm\W)|(\Wterm$) 

第一個條件檢查,以確保這個詞不是Blob的唯一內容,第二檢查自己的第一個字,第三個如果它包含在blob中,最後一個如果它是最後一個單詞。

如果您想將任何其他字符視爲單詞字符(如連字符),則需要將\W[^\w\-]重新分隔。

希望這會有所幫助。有可能優化也可以執行,但這至少應該是一個很好的起點。

+0

他也可以簡單地在'[']中包含'^'和'$' –

+2

^裏面[]意味着別的東西。 $將映射到美元符號。 然而你可以做一些像(^ | \ W)(term)(\ W | $) – Gerben

+0

@Gerben好多了!但是,再想一想,這個(以及我之前的模式)現在提出了另一個問題:非單詞字符也將包含在比賽中。這將需要額外的邏輯來排除它們... – Rodaine

8

假設所有的詞彙表中的詞彙由標準的「單詞」字符(即[A-Za-z0-9_])組成,那麼可以在正則表達式模式中的單詞前後放置一個簡單的單詞邊界斷言。嘗試與此更換pertinant聲明:

$element->innertext = preg_replace(
    '/\b'. $glossary_word .'\b/i', 
    '<a '. $glossary_tip .' >'. $glossary['word'] .'</a>', 
    $element->innertext); 

這假定$glossary_word已運行低谷preg_quote(你的代碼一樣)。但是,如果詞彙表單詞可能包含其他非標準單詞字符(例如'-'短劃線),則可以制定一個更復雜的正則表達式,其中包含前視和後視以確保只匹配整個單詞。例如:

$re_pattern = "/   # Match a glossary whole word. 
    (?<=[\s'\"]|^)  # Word preceded by whitespace, quote or BOS. 
    {$glossary_word}  # Word to be matched. 
    (?=[\s'\".?!,;:]|$) # Word followed by ws, quote, punct or EOS. 
    /ix"; 
+0

是的,我有這個麻煩是,單詞格式不匹配 –

+0

@Richard Housham:第二,更長的正則表達式將適用於_any_單詞(或甚至包含空間的短語)。 – ridgerunner

3

我在JS個人獲得的話這個問題。我做的是以下(你可以把它從JS翻譯成PHP):

它實際上對我來說真的很好。 :)

var words = document.body.innerHTML; 

// FIRST PASS 

// remove scripts 
words = words.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, ''); 
// remove CSS 
words = words.replace(/<style[\s\S]*?>[\s\S]*?<\/style>/gi, ''); 
// remove comments 
words = words.replace(/<!--[\s\S]*?-->/g, ''); 
// remove html character entities 
words = words.replace(/&.*?;/g, ' '); 
// remove all HTML 
words = words.replace(/<[\s\S]*?>/g, ''); 

// SECOND PASS 

// remove all newlines 
words = words.replace(/\n/g, ' '); 
// replace multiple spaces with 1 space 
words = words.replace(/\s{2,}/g, ' '); 

// split each word 
words = words.split(/[^a-z-']+/gi);