Пишем синонимайзер текста на PHP

Для начала нам неабходимо подобрать формат, в котором будут записаны слова.
Давайте остановимся на таком,

слово => синоним | синоним | синоним | ...
слово1 => синоним | синоним
слово2 => @слово
...

так как для работы с ним уже был написан код для другого проекта.

@собачка указывает что синоним является алиасом для другого синонима.

Поиски в сети не предоставили мне удобоваримых форматов словарей, поэтому пришлось писать конвертор найденного в наш формат.
Сниппет словаря, который нужено сконвертить в наш формат

белок# || вытаращить арабские белки
Белый#, белоснежный, светлый, седой. Белее снега. См. чистый || профессор белой и черной магии, сделать белее снега, сказка о белом бычке, сказка про белого бычка
бельмес# || ни бельмеса, ни бельмеса не смыслить
бельмо# || вылупить бельма, выпучить бельма, выпялить бельма, вытаращить бельма, налить бельма
беляк (-чка)# [опрятный человек, щеголек (Даль, белый)] см. франт
бенефис# || устроить бенефис
Бередить#, вередить, растравлять, растрагивать, задевать. Ср. <Возбуждать>.
Бережливость#, бережь, домовитость, расчетливость, экономия. "Бережь лучше прибытка" (посл.).
Бережливый#, бережный, домовитый, расчетливый, экономный, хозяйственный, умеренный. Жить с расчетом (бережливо). Ср. <Скупой>. Прот. <Расточительный>. См. скупой
бережь# [береженье и бережливость; охрана, сохранение; осторожность (Даль)] см. бережливость, заботливость

Пишем код конвертора

Файл dicconv.php - Конвертор словаря
<?php
setlocale (LC_ALL, 'ru_RU.CP-1251');
#исходный словарь
$buffer = file_get_contents("original.txt");
#step1
$buffer = preg_replace("/\[.*\] см\./mU", "", $buffer);
$buffer = preg_replace("/\(.*\)/U", "", $buffer);
$buffer = preg_replace("/\<.*\>/U", "", $buffer);
$buffer = preg_replace("/^(.*)\..*$/mU", "$1", $buffer);
$buffer = str_replace('||', '', $buffer);
$buffer = str_replace(array('#,', '# ,'), '#', $buffer);
$buffer = str_replace('#', ' =>', $buffer);
$buffer = str_replace(',', ' |', $buffer);
$buffer = strtolower($buffer);

$abuffer = explode("\n", $buffer);
$kbuffer = array();

foreach ($abuffer as $v) {
$_v = explode('=>', $v);
$_k = trim($_v[0]);
$_ak = explode('|', $_k);
$_k = preg_replace("#^([\w[:space:]]+).*$#", "$1", $_k);

$_v = trim($_v[1]);
if (!empty($_k) && !empty($_v)) {
  $kbuffer[$_k] = $_v;

  if (count($_ak) > 1) {
    array_shift($_ak);
    foreach ($_ak as $_akv) { if (($__akv = trim($_akv)) && !empty($__akv)) $kbuffer[$__akv] = "@{$_k}"; }
  }

}
}

uksort($kbuffer, "kcmp");
#implode to text
$buffer = '';
unset($abuffer);

foreach ($kbuffer as $k => $v) {
$buffer .= sprintf("%s => %s\n", $k, $v);
}

$buffer = preg_replace("#[\040]{2,}#", ' ', $buffer);

#done - кладем словарь в файл dic.txt
file_put_contents('dic.txt', $buffer);

/// callback
function kcmp($a, $b) { return strlen($a) > strlen($b); }


Имеем - словарь original.txt. Запускаем скрипт php ./dicconvert.php, на выходе получаем файл dic.txt,
ключи (синонимы) упорядочены по длине фразы - так чтобы короткие слова заменялись сразу, длинные - в конце.

белок => вытаращить арабские белки
волос => шерсть | щетина | пух | пушок | ворса; вихор | грива | хохол | челка | чуб | шевелюра | коса | борода | усы | бакенбарды | бакены | баки | висок | кудер | косма | прядь
антик => редкость | чудак
впрок => заготовлять впрок | не впрок
багаж => вещи | кладь | поклажа | клажа | ноша | груз | фрахт
вкось => и вкривь | и вкось
беляк => франт
...

Этот файл сьест в последствии наш обработчик словаря.

Класс synonimizer.class.php - Парсер словаря

Собственно библиотека для работы со словарем.
Класс может использоваться автономно, пример:
$syn = new synonimizer();
$result_text = $syn->syn("любой текст");

В конструкторе читаем файл dic.txt и разбирает его в массив внутреннего формата.
Для наблюдения за процессом предусмотрена константа DEBUG, установка которой выдаст при работе
в консоль отладочную информацию о выполненых заменах.
<?php

class synonimizer {

const DEBUG = 0;

const ROW_DELIM   = '=>';   
const VALUE_DELIM = '|';   

private $_dic_file = 'dic.txt';

/**
* @private array 'syn' = array(value, value, value)
*/
private $parsed = array();


function __construct() {
     
     $_parsed = file(dirname(__FILE__) . '/' . $this->_dic_file);
     
    foreach ($_parsed as $k => $v) {
      $v = trim($v);  
      if (empty($v) || 0 === strpos($v, '#')) {
      ; // nop
      }
      else {
        $v = explode(self::ROW_DELIM, $v);
        $_key = trim($v[0]);
        $_data = array();
        if (strpos($v[1], self::VALUE_DELIM) !== false) {
            $data = explode(self::VALUE_DELIM, $v[1]);
            foreach ($data as $dk => $dv) {
                $_data[$dk] = trim($dv);
            }
        }
        else {
            $_data = array(trim($v[1])); 
            if (strpos($_data[0], '@') === 0) {
                // this is alias
                $_data = $this->parsed[substr($_data[0], 1)];
            }
        }
      
        // save
        $this->parsed[$_key] = $_data;
      }
    }
}

/**
* Callback 
*/
static $_c_matches = false; 
static function syn_callback($matches) {
     $out = $matches[2];
     if (self::DEBUG) echo " -- " . $out . "\n";
     if (!empty(self::$_c_matches)) {
         $i = count(self::$_c_matches) - 1;
         $i = ($i > 0) ? mt_rand(0, $i) : 0;
         $out = self::$_c_matches[$i];
     }
     if (self::DEBUG)  echo " ++ " . $out . "\n\n";
     return $matches[1] . $out . $matches[3];
}

private static $index = 0;

public function syn($text) {

   $text = ' ' . str_replace(array('\r\n', '\n'), "\r\n", $text) . ' ';
   self::$index++; 
   echo ">>> " . self::$index . "\n";
   
   if (self::DEBUG) {
       echo str_repeat("-", 80) . "\n" . wordwrap($text, 80) . "\n" .  str_repeat("-", 80) . "\n\n";
    }

    foreach ($this->parsed as $key => $matches) {
        self::$_c_matches = &$matches;
        $text = preg_replace_callback(
        '#([^\w\d\-])(' . preg_quote($key) . ')([^\w\d\-])#i'// ([^\w\d\-]) //([\s\.\,])
        , 'synonimizer::syn_callback'
        , $text
        );
      
    }

    $text = str_replace("\r\n", '\r\n', substr($text, 1, -1));
    if (self::DEBUG) echo "--> " . wordwrap($text, 80) . " \n\n";
    return $text;
} 
}

Предполагается, что у нас имеется достаточно большой дамп таблицы в файле source.1251.sql.
В нем нам нужно отсинонимизировать поля title, text, description.
UPDATE `texts` SET `id` = 1,`title` = 'Под Тверью открылся рок-фестиваль "Эммаус-2006"', 
`description` = 'За два дня здесь ожидается \nвыступление более 20 начинающих групп. Как говорят зрители \nфестиваля: "В его афише хорошие  музыканты, это привлекает, как \nи само место проведения, все здорово!".'
WHERE  `ss_news_export_news`.`id` = 1;

UPDATE `texts` SET `id` = 2,`title` = 'Samsung представил новый игровой телефон',
`description` = 'Компания Samsung Electronics представила новый игровой сотовый телефон. Мобильник SCH-B450 будет отличаться стильным дизайном и широким набором мультимедийных функций. Видеоадаптер мобильника позволит запускать на SCH-B450 игры с трехмерной графикой, а сп',`category_id` = 2,
WHERE  `ss_news_export_news`.`id` = 2;

...




Файл synmake.php - Синонимайзер

На входе - дамп, на выходе - файл result.sql.
Разницу между файлами можно оценить под виндой например с помощью winmerge.
<?php
$source = "source.1251.sql";
require "synonimizer.class.php";
ob_implicit_flush(true);

$syn = new synonimizer();
$buffer = file_get_contents($source);

echo "working...\n";

  function kupper($matches) {
    return '. ' . ucfirst($matches[1]);
  }

// функция обратного вызова
  function kreplace($matches) 
  {
    GLOBAL $syn;
    $text = $syn->syn($matches[2]);
    $text = preg_replace_callback('#\. ([а-я]+)#', "kupper", $text);
    $text = ucfirst($text);
    $text = $matches[1] . $text . $matches[3];
    return $text;
  }

$buffer = preg_replace_callback(
              '#(`(?:title|text|description)` = \')(.*)(\')#U',
              "kreplace",
              $buffer);


file_put_contents('result.sql', $buffer);

Синонимайзер дополнительно делает прописными все буквы слов, идущих после точки или стоящих в начале предложения.

Стартовый файл start.bat
Для windows, юниксойды добавляют в начало #!/usr/local/bin/php и запускают
@echo off chcp 1251

php synmake.php

Ну вот и все .. Синонимайзер текста готов :)! Как видите ничего сложного здесь нет.
Теги:
синонимайзер, текст
Добавлено: 20 Мая 2018 07:01:26 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...