2011-06-29 112 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);