Пишем синонимайзер на PHP
Категория: / DEV Блог
/ PHP (LAMP)
Определимся с форматом словаря.
Я для себя остановился на таком,
так как для работы с ним уже был написан код для другого проекта.
@собачка указывает что синоним является алиасом для другого синонима.
Поиски в сети не предоставили мне удобоваримых форматов словарей, поэтому пришлось писать конвертор найденного в наш формат.
Сниппет словаря, который нужено сконвертить в наш формат
Строчим код конвертора
Файл dicconv.php - Конвертор словаря
Имеем - словарь original.txt. Запускаем скрипт php ./dicconvert.php, на выходе получаем файл dic.txt,
ключи (синонимы) упорядочены по длине фразы - так чтобы короткие слова заменялись сразу, длинные - в конце.
Этот файл съест в последствии наш обработчик словаря.
Класс synonimizer.class.php - Парсер словаря
Собственно библиотека для работы со словарем.
Класс может использоваться автономно, пример:
В конструкторе читаем файл dic.txt и разбирает его в миссив внутреннего фомата.
Для наблюдения за процессом предусмотрена константа DEBUG, установка которой выдаст при работе
в консоль отладочную информацию о выполненых заменах.
Предполагается, что у нас имеется достаточно большой дамп таблицы в файле source.1251.sql.
В нем нам нужно отсинонимизировать поля title, text, description.
Файл synmake.php - Синонимайзер
На входе - дамп, на выходе - файл result.sql.
Разницу между файлами можно оценить diffом или под виндой например с помощью winmerge.
Синонимайзер дополнительно сделает прописными все буквы слов, идущих после точки или стоящих в начале предложения.
Стартовый файл start.bat Для windows, юниксойды добавляют в начало #!/usr/local/bin/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.
Разницу между файлами можно оценить diffом или под виндой например с помощью 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
Запускаем, получаем результаты.
:-D