草庐IT

php - 关键字 highlight 是在 PHP preg_replace() 中高亮显示高亮

coder 2024-04-22 原文

我有一个小型搜索引擎在做它的事情,并且想要突出显示结果。我以为我已经解决了所有问题,直到我今天使用的一组关键字把它从水中吹了出来。

问题是 preg_replace() 循环遍历替换,后来的替换替换了我插入到之前的文本中的文本。使困惑?这是我的伪函数:

public function highlightKeywords ($data, $keywords = array()) {
    $find = array();
    $replace = array();
    $begin = "<span class=\"keywordHighlight\">";
    $end = "</span>";
    foreach ($keywords as $kw) {
        $find[] = '/' . str_replace("/", "\/", $kw) . '/iu';
        $replace[] = $begin . "\$0" . $end;
    }
    return preg_replace($find, $replace, $data);
}

好的,所以它在搜索“fred”和“dagg”时有效,但遗憾的是,在搜索“class”和“lass”和“as”时,它在突出显示“Joseph's Class Group”时遇到了一个真正的问题

Joseph's <span class="keywordHighlight">Cl</span><span <span c<span <span class="keywordHighlight">cl</span>ass="keywordHighlight">lass</span>="keywordHighlight">c<span <span class="keywordHighlight">cl</span>ass="keywordHighlight">lass</span></span>="keywordHighlight">ass</span> Group

我怎样才能让后面的替换只适用于非 HTML 组件,同时允许对整个匹配项进行标记?例如如果我正在搜索“cla”和“lass”,我希望“class”被完整突出显示,因为这两个搜索词都在其中,即使它们重叠,并且应用于第一个匹配项的突出显示具有“class” ",但那个不应突出显示。

感叹。

我宁愿使用 PHP 解决方案也不愿使用 jQuery(或任何客户端)解决方案。

注意:我尝试按长度对关键字进行排序,先排序长的,但这意味着交叉搜索不会突出显示,意思是“cla”和“lass”只是单词“class”的一部分会突出显示,它仍然谋杀了替换标签:(

编辑:我搞砸了,从铅笔和纸开始,胡乱乱说,想出了一些非常乏味的代码来解决这个问题。这不是很好,所以仍然非常感谢修剪/加快速度的建议:)

public function highlightKeywords ($data, $keywords = array()) {
    $find = array();
    $replace = array();
    $begin = "<span class=\"keywordHighlight\">";
    $end = "</span>";
    $hits = array();
    foreach ($keywords as $kw) {
        $offset = 0;
        while (($pos = stripos($data, $kw, $offset)) !== false) {
            $hits[] = array($pos, $pos + strlen($kw));
            $offset = $pos + 1;
        }
    }
    if ($hits) {
        usort($hits, function($a, $b) {
            if ($a[0] == $b[0]) {
                return 0;
            }
            return ($a[0] < $b[0]) ? -1 : 1;
        });
        $thisthat = array(0 => $begin, 1 => $end);
        for ($i = 0; $i < count($hits); $i++) {
            foreach ($thisthat as $key => $val) {
                $pos = $hits[$i][$key];
                $data = substr($data, 0, $pos) . $val . substr($data, $pos);
                for ($j = 0; $j < count($hits); $j++) {
                    if ($hits[$j][0] >= $pos) {
                        $hits[$j][0] += strlen($val);
                    }
                    if ($hits[$j][1] >= $pos) {
                        $hits[$j][1] += strlen($val);
                    }
                }
            }
        }
    }
    return $data;
}

最佳答案

我已经使用以下方法来解决这个问题:

<?php

$protected_matches = array();
function protect(&$matches) {
    global $protected_matches;
    return "\0" . array_push($protected_matches, $matches[0]) . "\0";
}
function restore(&$matches) {
    global $protected_matches;
    return '<span class="keywordHighlight">' .
              $protected_matches[$matches[1] - 1] . '</span>';
}

preg_replace_callback('/\x0(\d+)\x0/', 'restore',
    preg_replace_callback($patterns, 'protect', $target_string));

第一个 preg_replace_callback 取出所有匹配项并用空字节包裹的占位符替换它们;第二遍将它们替换为 span 标签。

编辑:忘记提及 $patterns 是按字符串长度从最长到最短排序的。

编辑;另一种解决方案

<?php
        function highlightKeywords($data, $keywords = array(),
            $prefix = '<span class="hilite">', $suffix = '</span>') {

        $datacopy = strtolower($data);
        $keywords = array_map('strtolower', $keywords);
        $start = array();
        $end   = array();

        foreach ($keywords as $keyword) {
            $offset = 0;
            $length = strlen($keyword);
            while (($pos = strpos($datacopy, $keyword, $offset)) !== false) {
                $start[] = $pos;
                $end[]   = $offset = $pos + $length;
            }
        }

        if (!count($start)) return $data;

        sort($start);
        sort($end);

        // Merge and sort start/end using negative values to identify endpoints
        $zipper = array();
        $i = 0;
        $n = count($end);

        while ($i < $n)
            $zipper[] = count($start) && $start[0] <= $end[$i]
                ? array_shift($start)
                : -$end[$i++];

        // EXAMPLE:
        // [ 9, 10, -14, -14, 81, 82, 86, -86, -86, -90, 99, -103 ]
        // take 9, discard 10, take -14, take -14, create pair,
        // take 81, discard 82, discard 86, take -86, take -86, take -90, create pair
        // take 99, take -103, create pair
        // result: [9,14], [81,90], [99,103]

        // Generate non-overlapping start/end pairs
        $a = array_shift($zipper);
        $z = $x = null;
        while ($x = array_shift($zipper)) {
            if ($x < 0)
                $z = $x;
            else if ($z) {
                $spans[] = array($a, -$z);
                $a = $x;
                $z = null;
            }
        }
        $spans[] = array($a, -$z);

        // Insert the prefix/suffix in the start/end locations
        $n = count($spans);
        while ($n--)
            $data = substr($data, 0, $spans[$n][0])
            . $prefix
            . substr($data, $spans[$n][0], $spans[$n][1] - $spans[$n][0])
            . $suffix
            . substr($data, $spans[$n][1]);

        return $data;
    }

关于php - 关键字 highlight 是在 PHP preg_replace() 中高亮显示高亮,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9087471/

有关php - 关键字 highlight 是在 PHP preg_replace() 中高亮显示高亮的更多相关文章

  1. ruby - Ruby 的 AST 中的 'send' 关键字是什么意思? - 2

    我正在尝试学习Ruby词法分析器和解析器(whitequarkparser)以了解更多有关从Ruby脚本进一步生成机器代码的过程。在解析以下Ruby代码字符串时。defadd(a,b)returna+bendputsadd1,2它导致以下S表达式符号。s(:begin,s(:def,:add,s(:args,s(:arg,:a),s(:arg,:b)),s(:return,s(:send,s(:lvar,:a),:+,s(:lvar,:b)))),s(:send,nil,:puts,s(:send,nil,:add,s(:int,1),s(:int,3))))任何人都可以向我解释生成的

  2. ruby 正则表达式 : replace double slashes in URL - 2

    除了协议(protocol)定义中的斜杠('http[s]://'、'ftp://'等)之外,我想替换URL中的所有多个斜杠。我该怎么做?此代码无一异常(exception)地替换:url.gsub(/\/\/+/,'/') 最佳答案 您只需排除任何以:开头的匹配项url.gsub(/([^:])\/\//,'\1/') 关于ruby正则表达式:replacedoubleslashesinURL,我们在StackOverflow上找到一个类似的问题: http

  3. ruby - 为什么 return 关键字会导致我的 'if block' 出现问题? - 2

    下面的代码工作正常:person={:a=>:A,:b=>:B,:c=>:C}berson={:a=>:A1,:b=>:B1,:c=>:C1}kerson=person.merge(berson)do|key,oldv,newv|ifkey==:aoldvelsifkey==:bnewvelsekeyendendputskerson.inspect但是如果我在“ifblock”中添加return,我会得到一个错误:person={:a=>:A,:b=>:B,:c=>:C}berson={:a=>:A1,:b=>:B1,:c=>:C1}kerson=person.merge(berson

  4. ruby - 在 Ruby 中跳过额外的关键字参数 - 2

    我定义了一个方法:defmethod(one:1,two:2)[one,two]end当我这样调用它时:methodone:'one',three:'three'我得到:ArgumentError:unknownkeyword:three我不想从散列中一个一个地提取所需的键或排除额外的键。除了像这样定义方法之外,有没有办法规避这种行为:defmethod(one:1,two:2,**other)[one,two,other]end 最佳答案 如果不想写**other中的other,可以省略。defmethod(one:1,two:2

  5. ruby-on-rails - 这个 C 和 PHP 程序员如何学习 Ruby 和 Rails? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我来自C、php和bash背景,很容易学习,因为它们都有相同的C结构,我可以将其与我已经知道的联系起来。然后2年前我学了Python并且学得很好,Python对我来说比Ruby更容易学。然后从去年开始,我一直在尝试学习Ruby,然后是Rails,我承认,直到现在我还是学不会,讽刺的是那些打着简单易学的烙印,但是对于我这样一个老练的程序员来说,我只是无法将它

  6. ruby - 是否有可能在 Ruby 中以哈希的形式访问关键字参数? - 2

    我知道我能做到:classParentdefinitialize(args)args.eachdo|k,v|instance_variable_set("@#{k}",v)endendendclassA但我想使用关键字参数来更清楚地说明可以接受哪个散列键方法(并进行验证表明不支持此键)。所以我可以写:classAdefinitialize(param1:3,param2:4)@param1=param1@param2=param2endend但是有没有可能写一些更短的东西而不是@x=x;@y=y;...从传递的关键字参数初始化实例变量?是否可以访问作为哈希传递的关键字参数?

  7. ruby :关键字 "in"是什么意思 - 2

    当我第一次在ruby​​中找到关键字“in”时。我想也许我可以这样做:1英寸(0..10)但看起来我不能那样使用它。然后我在ruby​​-lang.org中搜索它,然后用谷歌搜索它。没有答案!ruby中关键字“in”的含义是什么? 最佳答案 您应该能够执行以下操作:foriin0..10doputsiend您提到的表达式1in(0..10)将不起作用,因为常量(1)不能在一定范围内变化-它是一个常量!您需要在in关键字之前命名一个变量。希望对您有所帮助。参见thispage 关于ruby

  8. ruby - 为什么关键字参数必须作为带有符号键的散列传递,而不是 Ruby 中的字符串键? - 2

    我们不能将关键字参数作为带有字符串键的散列传递,关键字参数仅适用于作为符号键的散列。一个简单的例子:defmy_method(first_name:,last_name:)puts"first_name:#{first_name}|last_name:#{last_name}"endmy_method({last_name:'Sehrawat',first_name:'Manoj'})#=>first_name:Manoj|last_name:Sehrawatmy_method({first_name:'Bob',last_name:'Marley'})#=>first_name:Bo

  9. 对于体育新闻中文文本关键字提取有哪些关键字提取算法及其步骤 - 2

    对于体育新闻中文文本的关键字提取,常用的算法包括TF-IDF、TextRank和LDA等。它们的基本步骤如下:1.TF-IDF算法: -将文本进行分词和词性标注处理。-统计每个词在文本中的词频(TF)。-计算每个词在整个语料库中出现的文档频率(DF)和逆文档频率(IDF)。-计算每个词的TF-IDF值,并按照值的大小进行排序,选择排名前几的词作为关键字。2.TextRank算法:-将文本进行分词和词性标注处理。-将分词结果转化成图模型,每个词语为节点,根据词语之间的共现关系建立边。-对图模型进行迭代计算,计算每个节点的PageRank值,表示该节点的重要性。-选择排名前几的节点作为关键字。3.

  10. ruby - 从用户提交的文本中提取关键字的好方法是什么? - 2

    我正在构建一个网站,该网站允许用户通过以图形方式表示支持和反对特定问题的论点来理解辩论。(Wrangl)我想对这些辩论进行分类,以便更容易找到它们并将它们联系起来。我不想让发起辩论的人在他们看到任何好处之前添加标签和类别,从而激怒他们,所以我正在寻找一种自动提取关键字的方法。有什么好的方法可以利用辩论的标题和描述(以及可能的论点本身的内容,一旦有的话)来提取,比如说,可以用作元数据将类似辩论联系在一起的十个强关键字,或者即使是在可以查看辩论的HTML页面头部的“元”关键字标记的内容。例如。DatamappervsActiveRecord该网站使用Ruby和Sinatra编码,使用Dat

随机推荐