在HTML中搜索和替换单词

22
我正在尝试制作一个“行话破译器”。 基本上,我有一些HTML和数据库中的术语表。 当用户点击“行话破译器”时,它会用一个漂亮的工具提示(wztooltip)替换文本中的单词,并显示它们的含义。
我已经在努力地尝试了很久,重点关注了这个问题: Regex / DOMDocument - match and replace text not in a link 看起来答案在simple_html_dom libs中,但我无法让它正常工作。 显然,任何已经链接的单词都不会被更改。 以下是我所拥有的内容的简化版本。
$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();

我是一位同事。真正的问题是我们在努力使代码仅匹配个别单词,而不是单词内部的单词(即:也许中的APS)。这些单词也包含在HTML中。因此需要考虑这一点。 - David
这只是编写一个足够强大的正则表达式的问题,可能使用空格和标点符号来检测单词边界,尽管我不会尝试让自己尴尬。+1 - shanethehat
你需要一个 JS 解决方案还是一个 PHP 解决方案,因为你使用了这两个标签? - Gerben
你好,我之前写过一个维基媒体扩展,它做了类似的事情。根据你的方法,很容易得到一个效率低下的方案。你可以看一下这个链接,可能会有所帮助:https://github.com/bcoughlan/Extension-Lingo/blob/master/Lingo.php - bcoughlan
3个回答

11

使用倒置字符类\W在正则表达式模式中选择除数字和字母之外的任何字符。由于这仍然会在文本区块的边界处失败,因此您还需要测试这些条件。因此,以单词“term”作为要搜索的文本:

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

第一个条件检查是否term不是blob的唯一内容,第二个条件检查它是否是第一个单词,第三个条件检查它是否包含在blob内,最后一个条件检查它是否是最后一个单词。

如果要将其他字符视为单词字符(例如连字符),则需要用[^\w\-]替换\W

希望这可以帮到你。可能还有其他优化方法,但这至少应该是一个很好的起点。


他也可以简单地在 [] 中包含 ^$ - Felix Dombek
2
在方括号内的^表示其他含义。而$则代表美元符号。 你可以使用类似于(^|\W)(term)(\W|$)的语法。 - Gerben
@Gerben 好多了!但是,再考虑一下,这个(以及我之前的模式)现在又出现了另一个问题:非单词字符也会被包含在匹配中。这将需要额外的逻辑来排除它们... - Rodaine
1
下面的答案使用了\b,它是一个零长度(因此不会添加到匹配的组中)的特殊符号,表示单词边界(即\w\W相遇的地方,无论顺序如何)。你可能会发现这很有用。 - Steve Wang

8
假设您的词汇表中的“单词”都由标准的“单词”字符组成(即[A-Za-z0-9_]),那么可以在正则表达式模式中单词前后放置一个简单的单词边界断言。请尝试用以下内容替换相关语句:
$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";

是的,我遇到了这个问题,就是单词不符合单词格式。 - Richard Housham
@Richard Housham:第二个更长的正则表达式将适用于_任何_单词(甚至包含空格的短语)。 - 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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接