функция подсветки слов

dmx

Хранитель порядка
Регистрация
22 Июн 2011
Сообщения
715
Реакции
708
Нужно сделать подсветку ключевых слов в тексте
Найдена вот такая функция:

PHP:
<?php
/*
Функция подсветки слов
$text - текст
$keywords - ключевые слова

$onlystring - определяет способ вывода
  * false - выводим весь текст
  * true-выводим только строки, содержащие ключевые слова

$integrity - определяет параметры поиска
  * false - любое совпадение
  * true - фразу целиком

$fontcolor - каким цветом подсвечивать
*/
    function light_text($text, $keywords, $onlystring = false, $integrity = true, $fontcolor = "red")
    {//Пробелы
        $keywords = trim($keywords);
        $text = trim($text);
    //Проверка полученных данных
        if(empty($text) || empty($keywords))
            return false;
    //Спецсимволы PCRE
        $keywords = preg_quote($keywords, '#');

    //Ищем html, style, script, title
        $re = '#<script[^>]*?>.*?</script>|<style[^>]*?>.*?</style\s*?>|<title\s*?>.*?</title\s*?>|<.*?>#is';
        preg_match_all($re, $text, $html);
    //Удаляем одинаковые элементы, сортируем для того чтобы сбросить ключи
        $html = array_unique($html[0]);
        sort($html);

    //Массив с указателями
        $j = 1420;
        $i = 0;
        while($i<count($html))
        {
            $token[$i] = html_entity_decode('&#'. $j .';', ENT_NOQUOTES, 'UTF-8');
            $tags[$i] = $html[$i];
            $i++;
            $j++;
        }

    //Замена html, style, script, title указателями
        if(!empty($html))
            $text = str_replace($tags, $token, $text);

    //Поиск
        if(!empty($integrity))
        {//Если ищем всю фразу
            $re = '('. $keywords .')';
        }
        else
        {//Любое совпадение
        //Массив с ключевыми словами
            $keywords = explode(' ', $keywords);
        //Удаляем пустые элементы
            foreach($keywords as $k=>$v)
            {
                if(empty($v)) unset($keywords[$k]);
            }

            $re = '('. implode('|', $keywords) .')';
        }

        //Ищем и подсвечиваем
        if($onlystring)
        {//Если выводим только строки
            $re_for_string = '#.*'. $re .'.*#ui';
            preg_match_all($re_for_string, $text, $text);
            $text = implode("\n", $text[0]);
            if(!empty($token))
                $text = str_replace($token, '', $text);
        }

        $re = '#'. $re .'#uis';

        $text = preg_replace($re, '<font color="'. $fontcolor .'">\\1</font>', $text);
    //Вставляем html,css,js на место
        if(!empty($html))
            $text = str_replace($token, $html, $text);

    //Возвращаем
        return $text;

    }


$text = 'А вот не объявить ли новый конкурс к Новому году приуроченный?


Задачка небольшенькая по объему, но достаточно интересная по реализации.

Итак.
Требуется написать скрипт подсветки ключевых слов поиска.

Скрипт должен быть универсальным, в виде функции или класса, легко интегрируемый в любую систему.

Особенности таких скриптов, которые нужно учесть.

1. Подсвечиваться должны несколько ключевых слов.
2. Регистр не учитываем.
3. Слова могут повторяться и идти вразнобой.
4. Сам скрипт поиска не нужен, только механизм подсветки.
5. Результат примерно как в тексте задачи, если бы в скрипт передали фразу "скрипт подсветки слов поиска"


Ну разумеется корректность, оптимальность, безопасность, читабельность.
Стартуем, как наберем минимум 10 человек. Принимать участие могут все желающие.
Заявляться в этой ветке.

Оценивать будут члены жюри, желающие эксперты тоже здесь заявляйтесь.
Если желающих не будет, я сам вынесу вердикт.
Экспериментов больше не будет.

Ну и из песни слов не выкинуть - приз.
По традиции любая книга по программированию, которую можно купить в интернет-магазине.

Смелей, товарищи! Помните, главное не участие, главное победа!


PS Совсем забыл. Дополнительный функционал приветствуется. Допустим обрезка текста до и после найденных слов, настройка вариантов и прочее.
Вобщем чем круче скрипт, тем больше шансов.';

$keywords = 'скрипт подсветки слов поиска';

echo  light_text($text, $keywords);
подсвечивает только точное слово. Типа: слово
А надо чтоб учитывало и склонения или как их назвать. т.е полностью слово
так же, хотелось бы не подсвечивать слова меньше 2 символов (различные предлоги).
Ну и оставить только саму функцию, лишнего не надо ))
 
подсвечивает только точное слово. Типа: слово
А надо чтоб учитывало и склонения или как их назвать. т.е полностью слово
Понятно.
Ну я думаю тут вопрос гораздо шире — ведь эти словоформы должны и искаться в БД, и подсвечиваться.
Потому что поддержка русского языка при FULLTEXT поиске в MySQL мягко говоря — сосёт.
Наиболее близко подходит для этого Стеммер Портера.
Я переделывал и применял его в таком виде:
PHP:
class Stemmer {
    //Предлоги и стоп слова — будут заменены на пробел
    private $stop_words = array(
    '  ',' без ',' в ',' для ',' и ',' из ',' к ',' на ',' над ',' по ',' под ',' подле ',' про ',' при ',' с ',' о ',' от ',' у ',"\n","\r"
    );
    private $vowel = "аеёиоуыэюя";
    private $regexPerfectiveGerunds = array(
    "(в|вши|вшись)$",
    "(ив|ивши|ившись|ыв|ывши|ывшись)$"
    );
    private $regexAdjective = "(ее|ие|ые|ое|ими|ыми|ей|ий|ый|ой|ем|им|ым|ом|его|ого|ему|ому|их|ых|ую|юю|ая|яя|ою|ею)$";
    private $regexParticiple = array(
    "(ем|нн|вш|ющ|щ)",
    "(ивш|ывш|ующ)"
    );
    private $regexReflexives = "(ся|сь)$";
    private $regexVerb = array(
    "(ла|на|ете|йте|ли|й|л|ем|н|ло|но|ет|ют|ны|ть|ешь|нно)$",
    "(ила|ыла|ена|ейте|уйте|ите|или|ыли|ей|уй|ил|ыл|им|ым|ен|ило|ыло|ено|ят|ует|уют|ит|ыт|ены|ить|ыть|ишь|ую|ю)$"
    );
    private $regexNoun = "(а|ев|ов|ие|ье|е|иями|ями|ами|еи|ии|и|ией|ей|ой|ий|й|иям|ям|ием|ем|ам|ом|о|у|ах|иях|ях|ы|ь|ию|ью|ю|ия|ья|я)$";
    private $regexSuperlative = "(ейш|ейше)$";
    private $regexDerivational = "(ост|ость)$";
    private $regexI = "и$";
    private $regexNN = "нн$";
    private $regexSoftSign = "ь$";
    private $regexOkEk = "(ок|ек)$";
    private $word = '';
    private $RV = 0;
    private $R2 = 0;
  
    public function __construct($to_lower=true,$remove_punctuation=true,$replace_yo=true)    {
        //Приводить слова в нижний регистр?
        $this->to_lower = $to_lower;
        //Удалить пунктуацию?
        $this->remove_punctuation = $remove_punctuation;
        //Заменять ё на е?
        $this->replace_yo = $replace_yo;
    }
  
    public function getText($text,$limit=false,$strip_tags=true,$remove_stop_words=true,$before=' ',$after='')    {
        if ($strip_tags) $text = strip_tags($text);
        if ($remove_stop_words) $text = str_replace($this->stop_words, ' ',$text);
        $result = '';
        $words=explode(' ', $text);
        if (empty($words)) return '';
        //if ($limit) $words=array_slice($words, 0, $limit);
        foreach ($words as $word) {
            $word = $this->getWordBase($word);
            if (!empty($word)){
                if ($limit) $lim[] = $before.$word.$after;
                else $result .= $before.$word.$after;
            }
        }  
        if (!empty($lim)) $result.=implode('',array_slice($lim,0,$limit));
        return $result;
    }
  
    public function getWordBase($word)    {
        mb_internal_encoding('UTF-8');
        if (!empty($this->to_lower)) $word = mb_strtolower($word);
        if (!empty($this->remove_punctuation)) $word=str_replace(array('?','!',',','.',':',';','«','»',' —','"','\''), '', $word);
        if (!empty($this->replace_yo)) $word=str_replace('ё', 'е', $word);
        $this->word = trim($word);
        if (empty($word)) return '';
        $this->findRegions();
        //Шаг 1
        //Найти окончание PERFECTIVE GERUND. Если оно существует – удалить его и завершить этот шаг.
        if (!$this->removeEndings($this->regexPerfectiveGerunds, $this->RV)) {
            //Иначе, удаляем окончание REFLEXIVE (если оно существует).
            $this->removeEndings($this->regexReflexives, $this->RV);
            //Затем в следующем порядке пробуем удалить окончания: ADJECTIVAL, VERB, NOUN. Как только одно из них найдено – шаг завершается.
            if (!($this->removeEndings(
            array(
                $this->regexParticiple[0] . $this->regexAdjective,
                $this->regexParticiple[1] . $this->regexAdjective
            ),
            $this->RV
            ) || $this->removeEndings($this->regexAdjective, $this->RV))
            ) {
                if (!$this->removeEndings($this->regexVerb, $this->RV)) {
                    $this->removeEndings($this->regexNoun, $this->RV);
                }
            }
        }
        //Шаг 2
        //Если слово оканчивается на и – удаляем и.
        $this->removeEndings($this->regexI, $this->RV);
        //Шаг 3
        //Если в R2 найдется окончание DERIVATIONAL – удаляем его.
        $this->removeEndings($this->regexDerivational, $this->R2);
        //Шаг 4
        //Возможен один из трех вариантов:
        //Если слово оканчивается на нн – удаляем последнюю букву.
        if ($this->removeEndings($this->regexNN, $this->RV)) {
            $this->word .= 'н';
        }
        //Если слово оканчивается на SUPERLATIVE – удаляем его и снова удаляем последнюю букву, если слово оканчивается на нн.
        $this->removeEndings($this->regexSuperlative, $this->RV);
        //Если слово оканчивается на ь – удаляем его.
        $this->removeEndings($this->regexSoftSign, $this->RV);
        //Добавлена поправка удаления ок/ек
        $this->removeEndings($this->regexOkEk, $this->RV);
        return $this->word;
    }
  
    public function removeEndings($regex, $region)    {
        $prefix = mb_substr($this->word, 0, $region, 'utf8');
        $word = substr($this->word,strlen($prefix));
        if (is_array($regex)) {
            if (preg_match('/.+[а|я]' . $regex[0] . '/u', $word)) {
                $this->word = $prefix . preg_replace('/' . $regex[0] . '/u', '', $word);
                return true;
            }
            $regex = $regex[1];
        }
        if (preg_match('/.+' . $regex . '/u', $word)) {
            $this->word = $prefix . preg_replace('/' . $regex . '/u', '', $word);
            return true;
        }
        return false;
    }
  
    private function findRegions()    {
        $state = 0;
        $wordLength = mb_strlen($this->word, 'utf8');
        for ($i = 1; $i < $wordLength; $i++) {
            $prevChar = mb_substr($this->word, $i - 1, 1, 'utf8');
            $char = mb_substr($this->word, $i, 1, 'utf8');
            switch ($state) {
                case 0:
                    if ($this->isVowel($char)) {
                    $this->RV = $i + 1;
                    $state = 1;
                }
                break;
                case 1:
                if ($this->isVowel($prevChar) && !$this->isVowel($char)) {
                    $state = 2;
                }
                break;
                case 2:
                if ($this->isVowel($prevChar) && !$this->isVowel($char)) {
                    $this->R2 = $i + 1;
                return;
                }
                break;
            }
        }
    }
  
    private function isVowel($char) {
            return (strpos($this->vowel, $char) !== false);
    }
}

$text='  
<p class="showtext"><strong>На наших матрицах НЕТ битых пикселей в отличие от большинства конкурентов! Проверено!</strong></p>
<p class="helptext"><strong>В списке совместимостей с моделями ноутбуков указаны лишь некоторые (наиболее распространенные) модели ноутбуков, но это далеко не полный список. Если вы не знаете точно, подойдет ли данная матрица к вашему ноутбуку, свяжитесь с нами, мы вас обязательно проконсультируем.</strong></p><br>
<h2>Матрица 08.9" CLAA089NA0CCW Chunghwa - доп. информация :</h2>
<p class="helptext">Описанный на данной странице товар является новым и соответствует матрицам, устанавливаемым в серийные ноутбуки. На этот товар, как и на все остальные экраны для ноутбуков, действует <strong>гарантия от нашего магазина</strong>. <b>Матрица 08.9" CLAA089NA0CCW Chunghwa</b> - это оригинальный дисплей, совместимый со многими моделями ноутбуков. Если вы планировали купить данный товар, но в настоящее время его нет в наличии — свяжитесь с нами, и мы подберем для вас 100%-совместимую матрицу другого производителя.</p><br>
';
$stemmer = new Stemmer;
echo '<h2>Исходный текст:</h2><hr>'.$text.'<hr>';

echo '<h2>Результат</h2><hr>'.$stemmer->getText($text);
Этот пример берёт текст и прогоняет через Стеммер. В результате мы получаем набор словоформ, которые можно искать в тексте, подсвечивать и т.д.
Теги, окончания, знаки препинания и прочее вырезаются — остаются только корни слов.
Думаю, переделаете под себя без проблем.
 
Последнее редактирование:
  • Нравится
Реакции: dmx
В посте ограничение символов, потому дополню.
Алгоритм обычно такой, короче:
1. При сохранении текста обрабатываешь текст Стеммером и полученный набор корней слов сохраняешь в спец. поле для поиска.
2. При поиске основное поле текста не используется, а запрос обрабатывается Стеммером и потом ищется не в исходном тексте, а в спец. поле.
При этом спец. поле индексируешь в БД — т.к. там нет тегов и лишних знаков, это поле примерно в 5 раз меньше исходного текста.
В результате и поиск будет прост и быстр (не Фуллтекст, а простым LIKE с логическим OR между словами).
3. При выводе найденного текста подсвечиваешь слова своей функцией.
Вроде всё.
Изначальный функционал MySQL гораздо хуже. И это вряд ли лечится — когда искал подобное, предлагали Сфинкс и др. страшные вещи, недоступные на виртуальном хостинге.
 
  • Нравится
Реакции: dmx
Я бы решил проблему с окончаниями следующим образом: регуляркой искал "/($key.*)/" - любой символ но не пробел. Далее обрамляем полученное вхождение нужными стилями через функцию по замене.
 
  • Нравится
Реакции: dmx
Я бы решил проблему с окончаниями следующим образом: регуляркой искал "/($key.*)/" - любой символ но не пробел. Далее обрамляем полученное вхождение нужными стилями через функцию по замене.
воот!) можешь это оформить в коде?
 
PHP:
<?php
header("Content-Type: text/html; charset=utf-8");
$text = "я мудак и я ебу собак";
$key = "муд";


function lol($text,$key){
    if(iconv_strlen($key) > 2){
preg_match_all("|$key.*\s|Ui",$text,$match);
$style = '<span style="color:red">'.$match[0][0].'</span>';
$text = str_replace($match[0][0], $style, $text);
    }
    return $text;
}

echo lol($text,$key);
нужно доделать $key = array('муд','соб'); что бы с массивами работало, но я хочу баиньки :confused:
 
  • Нравится
Реакции: dmx
так как в $key будут попадать предлоги, нужно это вычистить. и независим от регистра.
нужно доделать $key = array('муд','соб'); что бы с массивами работало, но я хочу баиньки :confused:
буду ждать :)
 
PHP:
<?php
header("Content-Type: text/html; charset=utf-8");
$text = "я мудак и я ебу собак ";
$key = array('соб','муд');

function lol($text,$key){
   
    foreach($key as $key_ok){
    if(iconv_strlen($key_ok) > 2){
preg_match_all("|$key_ok.*\s|Ui",$text,$match);
$style = '<span style="color:red">'.$match[0][0].'</span>';
$text = str_replace($match[0][0], $style, $text);
    }
}
    
   return $text;
}

echo lol($text,$key);
?>
 
Назад
Сверху