Русская морфология

Captain Fizz

Новичок
Да, действительно, проблема была с путями... Пришлось сначала свалить все базы в одну кучу, потом прописывать путь отдельно для каждого параметра.

В итоге все переменные среды пришлось прописать в батниках.
 

Жигaн

Новичок
Captain Fizz
Ты переменные окружения устанавливаешь system wide, или через set?

Выложил новую версию
https://sourceforge.net/projects/phpmorphy/files/phpmorphy/0.3.5/phpmorphy-0.3.5.zip/download
где появились новые методы:

getAllFormsWithAncodes($word) - вывод похож на getAllFormsWithGramInfo, но возвращает анкоды. Настраивается при инициализации (см. ниже)

getAncode($word) - возвращает анкод для слова

getGramInfo($word) - возвращает грамматическую информацию для слова (часть речи, граммемы)

getGramInfoMergeForms($word) - похож на getGramInfo, но если слово имеет несколько интерпретаций внутри парадигмы, склеивает их (только граммемы)

function castFormByGramInfo($word, $partOfSpeech, $grammems, $returnOnlyWord = false, $callback = null, $type = self::NORMAL) - возвращает форму слова по заданным характеристикам. Если $partOfSpeech = null, часть речи игнорируется. Если $returnOnlyWord = true - возвращает только формы, иначе формы + грам.инфо. $callback - предикат, на фходе получет $form, $form_pos, $form_grammems, $form_no. Если $partOfSpeech установить в null, данная функция аналогична phpMorphy_TextTools::changeFormByGrammems(), но работать должна шустрее.

function castFormByPattern($word, $patternWord, $returnOnlyWord = false, $callback = null, $type = self::NORMAL)
- ставит слово $word в форму слова $patterWord. У слов должны совпадать ВСЕ характеристики (род, число, падеж, и т.п.). Это не аналог TextTools::changeFormByPattern

getAllFormsWithGramInfo - поменял сигнатуру на

function getAllFormsWithGramInfo($word, $asText = true, $type = self::NORMAL)

было

function getAllFormsWithGramInfo($word, $type = self::NORMAL)

добавил две опции инициализации

PHP:
$opts = array(
...
    'use_ancodes_cache' => true, // использовать кэш для анкодов,
    // ускоряет вызовы возвращающие грамматическую информацию на ~30%
    
    'resolve_ancodes' => $phpMorphy::RESOLVE_ANCODES_AS_TEXT,
    // настраивает вывод для getAllFormsWithAncodes, getAncode. 
    // значения:
    // phpMorphy::RESOLVE_ANCODES_AS_INT - использовать числовые идентификаторы
    // phpMorphy::RESOLVE_ANCODES_AS_DIALING - используются аношкинские коды (двухбуквенные)
    // phpMorphy::RESOLVE_ANCODES_AS_TEXT - анкод резолвится в ЧАСТЬ_РЕЧИ ГРАММЕМЫ_ЧЕРЕЗ_ЗАПЯТУЮ

);
для использования use_ancodes_cache необходимо создать файл с помощью команды extract_ancodes.bat КАТАЛОГ_СО_СЛОВАРЯМИ\morph_data.ru_ru.bin КАТАЛОГ_СО_СЛОВАРЯМИ

для phpMorphy::RESOLVE_ANCODES_AS_DIALING необходимо создать файл с помощью команды extract_ancodes_map.bat КАТАЛОГ_СО_СЛОВАРЯМИ\morph_data.ru_ru.bin Russian КАТАЛОГ_СО_СЛОВАРЯМИ

вроде все.
 

Captain Fizz

Новичок
Ты переменные окружения устанавливаешь system wide, или через set?
Через SET, уважаемый ;)

За новую версию респект, оттестирую анкоды по сравнению со старой и отпишу.

...

Подкинь пример использования функций castFormByGramInfo и castFormByPattern.

Новые методы воспроизвести получилось, а вот с функциями пока не очень понятно.
 

Жигaн

Новичок
PHP:
$word = 'ДУША';

// ДУША входит в парадигму слов ДУШ, ДУША, ДУШИТЬ. 
// здесь получим все формы существительных т.е. ДУШ, ДУША
// все формы сливаются в один массив.
var_dump($morphy->castFormByGramInfo('ДУША', 'С', null, true));

// то же самое, но результат дополняется 
// номером формы(form_no), частью речи(pos), 
// граммемами(grammems)
var_dump($morphy->castFormByGramInfo('ДУША', 'С', null, false));

// получим именительный падеж, единственное число для 
// существительных
// т.е. ДУШ и ДУША
var_dump($morphy->castFormByGramInfo('ДУША', 'С', array('ИМ', 'ЕД'), false));

// получим именительный падеж, единственное число для 
// любых частей речи в данном случае на выходе имеем
// ДУШ(сущ) + ДУША(сущ) + причастия для ДУШИТЬ

var_dump($morphy->castFormByGramInfo('ДУША', null, array('ИМ', 'ЕД'), false));

function validate_form($form, $form_pos, $form_grammems, $form_no) {
    return preg_match('~ИМ$~', $form); // все формы заканчивающиеся на ИМ

    // return $form_pos == 'С'; // все существительные
    // return $fom_no & 1 > 0; // все нечётные формы ;)
}

// отбираем формы используя функцию обратного вызова
var_dump($morphy->castFormByGramInfo('ДУША', null, null, false, 'validate_form'));

castFormByPattern ставит слово $word в форму слова $patternWord

PHP:
var_dump($morphy->castFormByPattern('ДУША', 'СТОЛОМ'));
// ДУШЕМ
// слово ДУШОЙ в выборку не попадает т.к. ДУША - женский род, а СТОЛ - мужской.

var_dump($morphy->castFormByPattern('МАША', 'СТОЛОМ'));
// пусто, потому-что МАША имеет помету ИМЯ, а СТОЛ нет.
 

Captain Fizz

Новичок
Спасибо за подробные примеры...

Проблема с компиляцией словарей, описанная мною выше, состояла в том, что косую черту в конце каталога при указании пути ставить НЕЛЬЗЯ.

То есть:
>build_dict ru_RU.xml c:\dicts\ windows-1251 - НЕ РАБОТАЕТ
>build_dict ru_RU.xml c:\dicts windows-1251 - РАБОТАЕТ

...............


Пожелания.

1. Хотелось бы увидеть в будущей версии функцию вроде:

PHP:
castFormByAncode($word, $Ancode, ...)
чтобы она возвращала форму слова по заданному анкоду.



2. Сделать варианты функций castFormByAncode и castFormByPattern для обаботки bulk words, то есть чтобы на вход можно было подавать массив.



3. Сделать для функции castFormByPattern() требования менее жесткие. Например сейчас эта функция возвращает результат при совпадении всех характеристик. Можно было бы возращать результат если допустим не совпадает род или число или пометка имени собственного.

Для рода:
для слов мир(м.р) - вселенной(ж.р.), castFormByPattern('мир','вселенной') должна возвратить 'миром'.

В примере с Машей то же самое, castFormByPattern('МАША', 'СТОЛОМ') должна возвратить 'Машей'


То есть в общем случае должны совпадать только части речи или даже падежи. Я в старой версии дополнительно ввел массив соответствия одинаковых анкодов одинаковым склонениям и когда склонял незнакомое слово, то выбирал из этого массива соответствующие падежи для других частей речи. Поясню на примере:

Был массив:

PHP:
$equ=array(

//  ======   СУЩЕСТВИТЕЛЬНЫЕ   ======== (возможно местоимения и числительные)
array('аа','Юо','го','ва','во','га','Йа','Йм','еа','Яз','бо','вО','до','Ра','Рж','Та','Тж'),			// сущ,ед,им
array('аб','Эф','Юп','гп','вб','вп','гб','Йб','Йн','еб','Яи','бп','вП','дп','Рб','Рз','Тб','Тз'),		// сущ,ед,рд
...

//  ======   ПРИЛАГАТЕЛЬНЫЕ   ========
array('йа','иа'),			// мр,ед,им,од,но
array('йб','иб'),			// мр,ед,рд,од,но
array('йв','ив'),			// мр,ед,дт,од,но
...

//  ======   ГЛАГОЛЫ   ========
array('кб','Ръ','Эе'),		// дст,нст,1л,ед
array('кв','Ры','Эж'),		// дст,нст,1л,мн
array('кг','Рэ','Эз'),		// дст,нст,2л,ед
...


);
Сначала определялся анкод исходного слова, например для 'маму' имеем анкод 'аб'
Потом возвращались все анкоды для слова-кандидата. Из этих всех форм выбиралась не только форма, соответствующая анкоду 'аб', но и все остальные из массива $equ[1], которые и возвращались в качестве результата.

Можно ли все это свести в одну функцию? Тут надо как-то все продумать. Массив $equ могу прислать.



4. Список всех доступных методов и функций с примерами (это больше относится к документированию). Пока отсутствие подробного описания на мой взгляд тормозит распространение этой библиотеки.

---------------------------------------------------

-~{}~ 01.09.09 23:22:

С первой функцией вроде все понятно, со второй проблема:

вызываю:

PHP:
 var_dump($morphy->castFormByPattern('МАША', 'НАТАШУ'));
на выходе ошибка Fatal error: Call to undefined method phpMorphy::getGrammarInfo() in \src\common.php on line 417

Первая функция в этих же условиях работает. (Однако ругается нотайсом Notice: Undefined variable: type in \src\common.php on line 375 при анализе $FormByGramInfo=$morphy->castFormByGramInfo('КРАКОЗЯБЛИКИ', null, array('ВН', 'МН'), false);
)
 

Жигaн

Новичок
Проблема с компиляцией словарей, описанная мною выше, состояла в том, что косую черту в конце каталога при указании пути ставить НЕЛЬЗЯ.

То есть:
>build_dict ru_RU.xml c:\dicts\ windows-1251 - НЕ РАБОТАЕТ
>build_dict ru_RU.xml c:\dicts windows-1251 - РАБОТАЕТ
Ага, спасибо. Проблема оказалась в escapeshellarg(), которая ничего не экранирует в windows версии. Запостил баг http://bugs.php.net/bug.php?id=49446&edit=2 , править пока не хотят ;).

1. Хотелось бы увидеть в будущей версии функцию вроде:

castFormByAncode($word, $Ancode, ...)
Я это могу сделать конечно, но не понятно зачем? Чем плох метод phpMorphy_TextTools::changeFormBy***? имхо удобнее работать на уровне граммем.

2. Сделать варианты функций castFormByAncode и castFormByPattern для обаботки bulk words, то есть чтобы на вход можно было подавать массив.
bulk mode был сделан для некоторых оптимизаций по скорости. Здесь такого наблюдаться не будет, так зачем оно надо?

3. Сделать для функции castFormByPattern() требования менее жесткие. Например сейчас эта функция возвращает результат при совпадении всех характеристик. Можно было бы возращать результат если допустим не совпадает род или число или пометка имени собственного.
Я сделаю приблизительно так, как сделано в phpMorphy_TextTools::changeFormByPattern()

var_dump($morphy->castFormByPattern('МАША', 'НАТАШУ'));

на выходе ошибка Fatal error: Call to undefined method phpMorphy::getGrammarInfo() in \src\common.php on line 417
Я дико извиняюсь. Тестировал новую систему сборки и забрал код из транка. Завтра выложу нормальный код.
 

Captain Fizz

Новичок
Владимир: ждем с нетерпением новой версии.

Функцию castFormByAncode($word, $Ancode, ...) я просил для того, чтобы иметь обратную к getAncode($word). Сдэлай дарагой, плз ;)

bulk режим я просил для оптимизации по скорости/возможного кеширования, но раз этого наблюдаться не будет, тогда и ни к чему оно.

Хорошо было бы новые функции сразу добавлять в класс TextTools ну и в дистрибутив его конечно ;).

.......................................


Для существительных, (а может и не только) несклоняемые формы вроде ДЕПО (0, но, ср), не обрабатываются функциями changeFormByххх.... Видимо это связано с тем, что в словаре представлена только одна форма слова ДЕПО на все случаи жизни. По идее функции вроде changeFormByGrammems('ДЕПО', array('РД')) на все падежи и вообще все запросы различных форм должны возвращать форму 'ДЕПО', а не null.
 

Жигaн

Новичок
0.3.6 версия
https://sourceforge.net/projects/phpmorphy/files/phpmorphy/0.3.6/phpmorphy-0.3.6.zip/download

- подчистил баги
- добавил возможность определять граммемы по которым идет поиск в castFormByPattern
- добавил castFormByAncode
- ф-ции getGramInfo[MergeForms](), getAncode возвращает неповторяющиеся данные.

Функцию castFormByAncode($word, $Ancode, ...) я просил для того, чтобы иметь обратную к getAncode($word).
сделал castFormByAncode($word, $ancode, $commonAncode, ...);

пример
PHP:
$r = $morphy->getAncode('ТЕСТОМ');
$r = $r[0];

var_dump(
    $r,
    $morphy->castFormByAncode('ТЕСТ', $r['all'][0], $r['common'])
);
Хорошо было бы новые функции сразу добавлять в класс TextTools ну и в дистрибутив его конечно
Поздно ;). всё идет в class phpMorphy

Для существительных, (а может и не только) несклоняемые формы вроде ДЕПО (0, но, ср)
Да, есть такая проблема. Пофиксил, но нужно обновить часть словаря. Для обновления необходимо выполниь команду
extract_gramtab.bat КАТАЛОГ_СО_СЛОВАРЯМИ\morph_data.ru_ru.bin КАТАЛОГ_СО_СЛОВАРЯМИ
тогда будет вот так:

PHP:
var_dump($morphy->getGramInfo('ДЕПО'));
var_dump($morphy->castFormByGramInfo('ДЕПО', null, 'РД'));
/*
array(1) {
  [0]=>
  array(1) {
    [0]=>
    array(3) {
      ["pos"]=>
      string(1) "С"
      ["grammems"]=>
      array(12) {
        [0]=>
        string(1) "0"
        [1]=>
        string(2) "ВН"
        [2]=>
        string(2) "ДТ"
        [3]=>
        string(2) "ЕД"
        [4]=>
        string(2) "ЗВ"
        [5]=>
        string(2) "ИМ"
        [6]=>
        string(2) "МН"
        [7]=>
        string(2) "НО"
        [8]=>
        string(2) "ПР"
        [9]=>
        string(2) "РД"
        [10]=>
        string(2) "СР"
        [11]=>
        string(2) "ТВ"
      }
      ["form_no"]=>
      int(0)
    }
  }
}
array(1) {
  [0]=>
  array(4) {
    ["form"]=>
    string(4) "ДЕПО"
    ["form_no"]=>
    int(0)
    ["pos"]=>
    string(1) "С"
    ["grammems"]=>
    array(12) {
      [0]=>
      string(2) "СР"
      [1]=>
      string(1) "0"
      [2]=>
      string(2) "ИМ"
      [3]=>
      string(2) "РД"
      [4]=>
      string(2) "ДТ"
      [5]=>
      string(2) "ВН"
      [6]=>
      string(2) "ТВ"
      [7]=>
      string(2) "ПР"
      [8]=>
      string(2) "ЗВ"
      [9]=>
      string(2) "МН"
      [10]=>
      string(2) "ЕД"
      [11]=>
      string(2) "НО"
    }
  }
}
*/
теперь о castFormByPattern

PHP:
// ставим слово СТУЛ в форму в которой находится слово 
// КРЕСЛО, на выходе ожидаем СТУЛОМ
var_dump($morphy->castFormByPattern('СТУЛ', 'КРЕСЛАМИ')); 
/*
вывод:
array(0) {
}

т.к. у слов СТУЛ и КРЕСЛО не совпадает род.
*/


$provider = $morphy->getGrammemsProvider();

// function excludeGroups($partOfSpeech, $groups)
// $partOfSpeech - часть речи для которой усекаем граммемы
// $groups - строка идентивицирующая группу граммем (или массив строк)
// аргументы должны быть в кодировке
// словаря. т.е. если работаем с utf-8 
// словарем 'С' и 'род' одлжны быть в utf-8. 

$provider->excludeGroups('С', 'род'); // не будем сравнивать род для существительных

/*
имена груп:
        'род' => array('МР', 'ЖР', 'СР'), 
        'одушевленность' => array('ОД', 'НО'), 
        'число' => array('ЕД', 'МН'), 
        'падеж' => array('ИМ', 'РД', 'ДТ', 'ВН', 'ТВ', 'ПР', 'ЗВ', '2'), 
        'залог' => array('ДСТ', 'СТР'), 
        'время' => array('НСТ', 'ПРШ', 'БУД'), 
        'повелительная форма' => array('ПВЛ'), 
        'лицо' => array('1Л', '2Л', '3Л'), 
        'краткость' => array('КР'), 
        'сравнительная форма' => array('СРАВН'), 
        'превосходная степень' => array('ПРЕВ'),
        'вид' => array('СВ', 'НС'),
        'переходность' => array('ПЕ', 'НП'),
        'безличный глагол' => array('БЕЗЛ'),
*/

var_dump($morphy->castFormByPattern('СТУЛ', 'КРЕСЛАМИ', $provider)); 

// на выходе получим СТУЛАМИ, СТУЛЬЯМИ
-~{}~ 01.10.09 21:59:

Выложил часть документации на http://phpmorphy.sourceforge.net/ там же есть демка http://phpmorphy.sourceforge.net/dokuwiki/demo
 

GTHack

Новичок
как можно весь словарь в нормальной форме выгрузить ?
(желательно только существительные)
 

Жигaн

Новичок
Rin
Я старался :)

GTHack
Можно сделать используя sql дамп . Запрос типа
Код:
select
*,
concat(f.prefix, l.base_str, f.suffix) form
from lemmas l
join flexias f on f.flexia_model_id=l.flexia_id
join ancodes a on a.id=f.ancode_id
join poses p on p.id=a.pos_id
where p.pos='С' and f.form_no=0
-- INTO OUTFILE '...'
zerkms
Спасибо, пофиксил.
 

Tamper

Новичок
Добрый день. Есть проблема, быть может она банальна. Но тем не менее. Проект (большинство файлов, база) в утф-8. Ошибка при тестировании(example.php) вот такая -

Error occured while text processing: Can`t read 128 bytes at 0 offset, from путь/phpmorphy/examples/../dicts/predict_aut.ru_ru.bin' file

Сделал трассировку исключения, которое выбрасывается. Выбрасывается оно тут (в example.php):

// You can also retrieve all word forms with graminfo via getAllFormsWithGramInfo method call
// $all_forms_with_gram = $morphy->getAllFormsWithGramInfo($word_one);
} catch(phpMorphy_Exception $e) {
die('Error occured while text processing: ' . $e->getMessage());
}

Докопал просто до вот этого метода в файле storage.php

function readUnsafe($offset, $len) {
fseek($this->resource, $offset);
return fread($this->resource, $len);
}

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

Пробовал делать иконвы в утф 8 слов, которые тестятся в example.php, а также переводить сам файл example.php в утф - 8. В случае, когда сам example.php в утф-8, переводил иконвом в цп1251 тестируемые слова.

Может кто-то подскажет решение?)
 

Жигaн

Новичок
Что-то подобное было... причина была в заливке словарей по фтп в текстовом режиме. Надо использовать бинарный режим. А еще лучше зайти по ssh и выполнить
unzip архив_со_словарём.zip.
 

Firestarter

Новичок
Доброго времину суток, помогите пожалуйста установить PHPMorphy.
все делаю как надо, но выскакивает ошибка:
Error occured while creating phpMorphy instance: Invalid fsa format

А когда комментирую строку:
PHP:
try{ 	$morphy = new phpMorphy($dir,$lang,$opts); } catch(phpMorphy_Exception $e)  { 	//die('Error occured while creating phpMorphy instance: ' . $e->getMessage()); }
Notice: Undefined variable: morphy in Z:\home\mor.log\www\examples\example.php on line 60 (не находит онпеременную)
Fatal error: Call to a member function getBaseForm() on a non-object in Z:\home\mor.log\www\examples\example.php on line 60
Все это творится на Denwer'e, помогите пожалуйста. Сил больше нету
 

Tamper

Новичок
Спасибо, а где можно последнюю версию словарей забрать? В самом приложении вроде нету их.

-~{}~ 09.10.09 13:20:

нашел, раззиповал архив, зайдя по ssh. Но таки все равно та же проблема
 

Жигaн

Новичок
замените метод phpMorphy_Storage::read в файле storage.php на

PHP:
    function read($offset, $len, $exactLength = true) {
        if($offset >= $this->getFileSize()) {
            throw new phpMorphy_Exception("Can`t read $len bytes beyond end of '" . $this->getFileName() . "' file, offset = $offset, file_size = " . $this->getFileSize());
        }

        try {
            $result = $this->readUnsafe($offset, $len);
        } catch (Exception $e) {
            throw new phpMorphy_Exception("Can`t read $len bytes at $offset offset, from '" . $this->getFileName() . "' file: " . $e->getMessage());
        }

        var_dump("Need $len, read (" . gettype($result) . ") " . strlen($result));

        if($exactLength && strlen($result) < $len) {
            throw new phpMorphy_Exception("Can`t read $len bytes at $offset offset, from '" . $this->getFileName() . "' file");
        }
            
        return $result;
    }
и результат запостите сюда + версию пхп на всякий случай
 

Tamper

Новичок
Сделал - результат:

string(27) "Need 128, read (string) 128" Testing single mode... string(27) "Need 128, read (string) 127" Error occured while text processing: Can`t read 128 bytes at 0 offset, from 'путь/phpmorphy/examples/../dicts/predict_aut.ru_ru.bin' file

Версия пхп - 5.2.9
 

Жигaн

Новичок
Хм, странно. Вы точно скачали/распаковали нужный архив?

Скачал сейчас словарь для windows-1251: https://sourceforge.net/projects/phpmorphy/files/phpmorphy-dictionaries/0.3.x/ru_RU/morphy-0.3.x-ru_RU-withjo-windows-1251.zip/download
отсюда
http://phpmorphy.sourceforge.net/dokuwiki/download

md5sum для содержимого архива, на всякий случай
<code>
42ab69e79bfc1fec1e1e228f65a1f2be *common_aut.ru_ru.bin
aef6b6573b60f5875be947e1889205ed *gramtab.ru_ru.bin
5a775531687a06fb03c55b5c68499b95 *gramtab_txt.ru_ru.bin
36094fdb06664bfdc3d4e585639b7594 *morph_data.ru_ru.bin
9e2bd90d5b45d37353ae83e77bdbc7c3 *morph_data_header_cache.ru_ru.bin
9dd92da7f050e93262f2106ebb5dfc21 *options.ru_ru.ini
bb68a1199673e646f5b3943ab50ce3eb *predict_aut.ru_ru.bin
</code>

Какой тип хранилища используется?
1) PHPMORPHY_STORAGE_SHM
попробуйте выполнить код

PHP:
try {
    $morphy = new phpMorphy($dir, $lang, $opts);
} catch(phpMorphy_Exception $e) {
    die('Error occured while creating phpMorphy instance: ' . (string)$e);
}

$morphy->getShmCache()->free();
exit;
он очистит кэш.

2) PHPMORPHY_STORAGE_FILE
почему читаются только 127 байт для predict_aut.ru_ru.bin, проверьте magic_quotes_runtime. Попробуйте прочитать его скриптом

PHP:
<?php
// относительно директории examples
$file = dirname(__FILE__) . '/../dicts/predict_aut.ru_ru.bin';
$fh = fopen($file, 'rb');
$seek_result = fseek($fh, 0);
var_dump($seek_result, strlen(fread($fh, 128)));
3) PHPMORPHY_STORAGE_MEM
тут в принципе аналогично п2

Попробуйте поменять storage в example.php
 
Сверху