24 января 2015 г.

LingvoNET - склонение слов на C#

Знаю, что найти хорошую библиотеку для морфинга(склонения и спряжения) слов русского языка - непросто. Так случилось, что ни одна мне не подошла, и пришлось писать свое. Сейчас решил выложить в открытый доступ.
Итак....

LingvoNET - Библиотека для склонения и спряжения слов русского языка для .NET Framework


Библиотека содержит встроенные словари глаголов, существительных, прилагательных и местоимений русского языка на основе небезызвестного словаря Зализняка. Написна на C#.

Реализован следующий функционал:
  • Точный и эвристический поиск основной формы существительных, глаголов, прилагательных в словаре.
  • Определение рода и одушевленности существительного, вида глагола (совершенный, несовершенный), валентности глагола(переходный, непереходный), разряд прилагательного (качественные, относительные).
  • Склонение существительных по падежам.
  • Спряжение глаголов по лицам, числу, времени, родам. Генерация активного и пассивного залогов.
  • Генерация указательных форм глагола, спряжение их по числу и залогу.
  • Генерация причастий из глаголов. Склонение их по залогам, падежам, родам, числу и времени. Генерация краткой формы причастия.
  • Генерация деепричастий из глаголов. Спряжение их по времени.
  • Генерация совершенного вида глаголов из несовершенного.
  • Генерация сослагательного/условного наклонения глаголов.
  • Склонение прилагательных по падежам. Генерация краткой формы прилагательного. Генерация сравнительной степени прилагательного. 
  • Генерация сравнительных степеней наречий.
  • Генерация личных, притяжательных, возвратных и притяжательно-возвратных местоимений. Склонение их по падежам, лицам, родам, числу.
  • Стеммер.
Основное назначение библиотеки - генерация читабельного текста в играх, системах искусственного интеллекта, агентных системах. Исходными данными может являться семантическая сеть либо база данных. Библиотека НЕ предназначена для анализа текста.

Словарь содержит около 46 тысяч существительных, 27 тысяч глаголов, 20 тысяч прилагательных.

Библиотека написана на C#, под .NET Framework 3.5. Распространяется без исходного кода. Размер dll - около 530 КБ, никакие дополнительные файлы не требуются. 100% управляемый код. Библиотека бесплатна для некоммерческого использования.

Далее приведу более подробно основные классы и методы библиотеки, примеры кода.

 

Существительные

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

Nouns содержит следующие методы:

public static Noun FindOne(string sourceForm, Gender gender = Gender.Undefined, Animacy animacy = Animacy.Undefined){...}
public static IEnumerable<Noun> FindAll(string sourceForm){...}
public static Noun FindSimilar(string sourceForm, Gender gender = Gender.Undefined, Animacy animacy = Animacy.Undefined){...}

Методы возвращают объект типа Noun, который инкапсулирует в себе грамматические формы найденного слова.
В методы передается sourceForm - исходное существительное по которому происходит поиск. Исходная форма должна быть в единственном числе и именительном падеже. Регистр исходного слова для поиска значения не имеет, однако если например, первая буква будет большой, то генерируемые словоформы также будут начинаться с большой буквы. Это удобно для склонения имен собственных.
Исходная форма слова всегда должна быть в единственном числе, за исключением тех существительных, которые существуют только во множественном числе (например слово "чернила"). В таком случае следует передавать в sourceForm множественное число.
Исходная форма должна содержать ровно одно слово, несколько слов разделенных пробелами - не допускаются. Допускается дефис, если в словаре это слово приведено с дефисом (например "стоп-сигнал").

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

Метод FindOne возвращает ровно одно найденное слово, либо null, если ни одного слова не найдено. При этом в параметрах метода вы можете указать конкретные критерии выбора нужного слова среди омонимичнх слов. Например, вам нужно просклонять слово "ласка". Поскольку в словаре содержится как минимум два омонима для этого слова (один - ласка - животное, другой - ласка - синоним нежности), то вы можете явно указать одушевленность слова - передав параметр Animacy.Animated (одушевленный). Аналогично - род и число.
Если же вы хотите получить все омонимы слова - вызовите метод FindAll.

Понятно, что не все слова есть в словаре. Для склонения неизвестных слов в LingvoNET реализован эвристический поиск. Среди словарных слов ищется наиболее похожее слово с наиболее близким окончанием. Из этого слова извлекается схема морфинга и она применяется к исходному слову. Для поиска наиболее похожего слова используйте метод FindSimilar.
Этот метод также содержит фильтр, с помощью которого вы можете влиять на поиск похожих слов по своим критериям (например по роду или числу). Метод FindSimilar возвращает один объект Noun, либо возвращает null, если похожих слов не найдено. Слова считаются похожими, если как минимум две буквы окончания совпадают. Метод также вернет точное сответствие, если искомое слово есть в словаре. Вы можете определить, является ли найденное слово словарным, по свойству Noun.Inexact. Оно равно true, если слова нет в словаре и был использован эвристический поиск.

Время поиска в словаре - O(log n).

Класс Noun содержит информацию о найденом слове и его словоформы.
Свойство

public Gender Gender { get; } 

возвращает род, число и одушевленность существительного.
 
Род и число существительного объединены в одно перечисление, поскольку эти формы никогда не пересекаются - множественное число в русском языке не может иметь рода.
Одушевленность существительного необходима, поскольку влияет на склонение (в основном винительного падежа).

Для получения склонения в нужном падеже и числе, используйте следующий индексатор Noun:
 
public string this[Case @case, Number number = Number.Singular]{ get; }

Индексатор возвращает строку - склонение исходного существительного. Если данного склонения не существует - возвращается null.
 
В целом, пример кода для склонения выглядит так:

var noun = Nouns.FindOne("ласка", animacy : Animacy.Animate);//поиск одушевленного омонима слова "ласка"
if (noun != null)
{
    //получаем винительный падеж множественного числа
    var accusative = noun[Case.Accusative, Number.Plural]; // accusative -> (кого?) ласок
    //....
}

Ниже приведен результат склонения всех омономов для слова "ласка", полученых методом FindAll:

---------------------------------Noun FA 

Nominative:   ласка             ласки          
Genitive:     ласки             ласок          
Dative:       ласке             ласкам         
Accusative:   ласку             ласок          
Instrumental: лаской            ласками        
Locative:     ласке             ласках         

---------------------------------Noun F 

Nominative:   ласка             ласки          
Genitive:     ласки             ласк           
Dative:       ласке             ласкам         
Accusative:   ласку             ласки          
Instrumental: лаской            ласками        
Locative:     ласке             ласках   


Найдено два омонима. Первый имеет женский одушевленый род (FA). Второй - женский неодушевленный (F). Первый столбец показывает склонение единственного числа, второй - множественного. Омонимы имеют отличия склонения в винительном падеже (Accusative) множественного числа.

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

Если слово отсутсвует в словаре, происходит поиск максимально похожего слова. Ниже показан результат склонения слова "Петр", которого нет в словаре:

---------------------------------Noun MA (inexactly)

Nominative:   Петр              Петры          
Genitive:     Петра             Петров         
Dative:       Петру             Петрам         
Accusative:   Петра             Петров         
Instrumental: Петром            Петрами        
Locative:     Петре             Петрах         

Склонения получены методом FindSimilar с фильтром на одушевленный род. Сноска inexactly указывает на то, что результат получен эвристическим методом - по похожему слову.
 
 

Глаголы

Статический класс Verbs содерждит словарь глаголов. Аналогично существительным, этот класс содержит три метода для поиска слов:

public static Verb FindOne(string sourceForm, VerbAspect aspect = VerbAspect.Undefined){...}
public static IEnumerable<Verb> FindAll(string sourceForm){...}
public static Verb FindSimilar(string sourceForm, VerbAspect aspect = VerbAspect.Undefined){...}

Семантика методов - аналогична словарю существительных.
Фильтрацию глаголов можно производить по параметру вида глагола (совершенный, несовершенный).

Параметр sourceForm должен содержать глагол в неопределнной форме (инфинитив).

Результатом поиска глаголов является объект типа Verb, который содержит информацию о глаголе и его словоформы.
Объект Verb имеет свойства Verb.Aspect (вид глагола) и Verb.Transitive (валентность глагола).
Для спряжения глагола по залогу, лицу и числу используйте следующий индексатор Verb:

public string this[Voice voice, Person person, Number number]{get;}

Индексатор возвращает строку - словоформу в нужном спряжении. Если спряжения не существует - возвращается null.
Для получения других форм глагола существуют следующие методы:

/// <summary>
/// Указательная форма глагола (спряжение по залогу и числу)
/// </summary>
public string Imperative(Voice voice, Number number){...}
 
/// <summary>
/// Прошедшее время глагола (спряжение по залогу и роду)
/// </summary>
public string Past(Voice voice, Gender gender){...}
 
/// <summary>
/// Деепричастие (спряжение по времени)
/// </summary>
public string Gerund(Tense tense){...}
 
/// <summary>
/// Причастие (спряжение по залогу, падежу, роду и времени)
/// </summary>
public string Participle(Voice voice, Case @case, Gender gender, Tense tense){...}
Смысл методов понятен по комментариям. Если спряжения не существует - возвращается null.

Для получения будущего времени глагола - используйте методы Future() и FuturePerfect(). Первый из них генерирует будущее время с помощью служебного слова, второй - с помощью глагола совершенного вида.

Для образования совершенной формы из несовершенной - используйте методы Perfect() и Perfects().

Для получения сослагательного/условного наклонения - используйте метод Subjunctive().

Ниже приведен пример кода для спряжения глагола "рисовать":
var verb = Verbs.FindOne("рисовать");//поиск глагола "рисовать"
if (verb != null)
{
    //получаем спряжение во втором лице множественного числа активного залога
    var secondPerson = verb[Voice.Active, Person.Second, Number.Plural]; // secondPerson -> (вы) рисуете
    //....
}
 
Все формы глагола (кроме деепричастия) делятся по залогу. Поэтому LingvoNET выдает все спряжения глагола в виде двух больших групп - активный залог и пассивный залог. В зависимости от вида и валентности глагола, часть спряжений может отсутствовать (особенно часто у совершенных глаголов) - как в активном, так и в пассивном залогах. Краткая форма причастия всегда отсутсвует в активном залоге.

Ниже приведен результат спряжения глагола "рисовать":
---------------------------------Verb Imperfect Transitive 
----Active:   рисовать       

Present
First:    рисую            рисуем         
Second:   рисуешь          рисуете        
Third:    рисует           рисуют         

Future
First:    буду рисовать         будем рисовать          нарисую          нарисуем
Second:   будешь рисовать       будете рисовать         нарисуешь        нарисуете
Third:    будет рисовать        будут рисовать          нарисует         нарисуют

Past:     рисовал          рисовала         рисовало         рисовали       

Perfect:     нарисовать     
Gerund:      рисуя             рисовав        
Imperative:  рисуй             рисуйте        

Present participle
Nominative:            рисующий          рисующая          рисующее          рисующие
Genitive:              рисующего         рисующей          рисующего         рисующих
Dative:                рисующему         рисующей          рисующему         рисующим
Accusative Inanimate:  рисующий          рисующую          рисующее          рисующие
Accusative Animal:     рисующего         рисующую          рисующее          рисующих
Instrumental:          рисующим          рисующей          рисующим          рисующими
Locative:              рисующем          рисующей          рисующем          рисующих
Short:                                                                                      


Past participle
Nominative:            рисовавший        рисовавшая        рисовавшее        рисовавшие
Genitive:              рисовавшего       рисовавшей        рисовавшего       рисовавших
Dative:                рисовавшему       рисовавшей        рисовавшему       рисовавшим
Accusative Inanimate:  рисовавший        рисовавшую        рисовавшее        рисовавшие
Accusative Animal:     рисовавшего       рисовавшую        рисовавшее        рисовавших
Instrumental:          рисовавшим        рисовавшей        рисовавшим        рисовавшими
Locative:              рисовавшем        рисовавшей        рисовавшем        рисовавших 
Short:                                                                                      

----Passive:   рисоваться     

Present
First:    рисуюсь          рисуемся       
Second:   рисуешься        рисуетесь      
Third:    рисуется         рисуются       

Future
First:    буду рисоваться       будем рисоваться                                        
Second:   будешь рисоваться     будете рисоваться                                       
Third:    будет рисоваться      будут рисоваться                                        

Past:     рисовался        рисовалась       рисовалось       рисовались     

Perfect:     
Imperative:  рисуйся           рисуйтесь      

Present participle
Nominative:            рисуемый          рисуемая          рисуемое          рисуемые
Genitive:              рисуемого         рисуемой          рисуемого         рисуемых
Dative:                рисуемому         рисуемой          рисуемому         рисуемым
Accusative Inanimate:  рисуемый          рисуемую          рисуемое          рисуемые
Accusative Animal:     рисуемого         рисуемую          рисуемое          рисуемых
Instrumental:          рисуемым          рисуемой          рисуемым          рисуемыми
Locative:              рисуемом          рисуемой          рисуемом          рисуемых
Short:                 рисуем            рисуема           рисуемо           рисуемы


Past participle
Nominative:            рисованный        рисованная        рисованное        рисованные
Genitive:              рисованного       рисованной        рисованного       рисованных
Dative:                рисованному       рисованной        рисованному       рисованным
Accusative Inanimate:  рисованный        рисованную        рисованное        рисованные
Accusative Animal:     рисованного       рисованную        рисованное        рисованных
Instrumental:          рисованным        рисованной        рисованным        рисованными
Locative:              рисованном        рисованной        рисованном        рисованных
Short:                 рисован           рисована          рисовано          рисованы

Таблица показывает, что данный глагол несовершенного вида (Imperfect), переходной (Transitive). Таблица разбита на две части - активный (Active) и пассивный (Passive) залог. Деепричастие (Gerund) указано только в активном залоге. Причастия (Participle) склоняются по падежам, родам, числам и по времени.
Обратите внимание: винительный падеж причастия бывет разный в зависимости от  одушевленности. Поэтому таблица содержит два винительных падежа - неодушевленный(Accusative Inanimate) и одушевленный(Accusative Animal).


Прилагательные

Статический класс Adjectives содерждит словарь существительных. Класс содержит следующие методы:
 
public static Adjective FindOne(string sourceForm, Comparability comparability = Comparability.Undefined){...}
public static IEnumerable<Adjective> FindAll(string sourceForm){...}
public static Adjective FindSimilar(string sourceForm, Comparability comparability = Comparability.Undefined){...}

Смысл методов - аналогичен словарю существительных.

Исходная форма слова sourceForm должна передаваться в единственном числе мужского рода именительного падежа.

Методы возвращают объект типа Adjective, который инкапсулирует в себе грамматические формы найденного слова. Этот класс содержит в себе свойство Comparability, которое указывает на разряд найденного прилагательного.

Для получения склонения в нужном падеже, роде и числе, используйте следующий индексатор Adjective:

public string this[Case @case, Gender gender]{get;}

Этот индексатор возвращает строку - словоформу прилагательного в нужном падеже и роде. Если данная форма прилагательного не найдена - возвращается null.
Помимо склонения, данный индексатор генерирует краткую форму прилагательного, которая возвращается при указании Case.Short.Для получения сравнительных степеней прилагательного, используйте следующий индексатор: 
public string this[Comparison comparsion]{get;}

В целом, код для склонения прилагательного может выглядеть так:

var adj = Adjectives.FindOne("красивый");//поиск прилагательного "красивый"
if (adj != null)
{
    //получаем родительный падеж женского рода
    var genetive = adj[Case.Genitive, Gender.F]; // genetive -> (какой?) красивой
    //....
}

Ниже приведен результат склонения слова "красивый", полученого методом FindAll:
---------------------------------Adj Comparable 

Nominative:            красивый          красивая          красивое          красивые
Genitive:              красивого         красивой          красивого         красивых
Dative:                красивому         красивой          красивому         красивым
Accusative Inanimate:  красивый          красивую          красивое          красивые
Accusative Animal:     красивого         красивую          красивое          красивых
Instrumental:          красивым          красивой          красивым          красивыми
Locative:              красивом          красивой          красивом          красивых
Short:                 красив            красива           красиво           красивы

Comparative:           красивее          покрасивее        красивей          покрасивей
Обратите внимание, что винительный падеж прилагательного зависит от одушевленности существительного к которому оно относится. Поэтому LingvoNET выдает два варианта склонения для винительного падежа - неодушевленное (Inanimate) и одушевленное (Animal).

Также выдается четыре типа сравнительной степени (Comparative) и краткая форма (Short) прилагательного по родам.
 
Иногда прилагательное может выступать как существительное. В таком случае необходимо пользоваться поиском в словаре существительных Nouns.
 

Наречия

LingvoNET не содержит словаря наречий, поскольку это неизменяемая часть речи. Однако вы можете определить разряд наречия и образовать сравнительную степень наречия.
 
Пример кода для образования сравнительной степени наречия:
 
var adv = Adverbs.FindOne("быстро");//поиск наречия "быстро"
//получаем одну из сравнительных степеней (всего их 5)
var comp = adv[Comparison.Comparative1]; // comp -> быстрее
//....
 
Класс Adverb генерирует пять вариантов сравнительной степени наречия. Генерация происходит только для качественных (Comparable) наречий. Для остальных - возвращается пустая строка.
 

Местоимения

LingvoNET содержит базу местоимений, которая инкапсулирована в статическом классе Pronouns. Этот класс не содержит методов Find. Вместо этого вы напрямую можете генерировать местоимения:

/// <summary>
/// Личные местоимения (я, ты, он, она, они, и т.д.)
/// </summary>
/// <param name="gender">Род только для 3 лица</param>
/// <param name="preposition">С предлогом</param>
public static string Personal(Case @case, Person person, Gender gender = Gender.M, bool preposition = false){...}
 
/// <summary>
/// Возвратные местоимения (себя)
/// </summary>
public static string Reflexive(Case @case){...}
 
/// <summary>
/// Притяжательные местоимения (мой, твой, его, и т.д.)
/// </summary>
/// <param name="person">Лицо говорящего. Не используется для number = Number.Plural</param>
/// <param name="number">Число (ед: мой твой его, множ: ваш)</param>
public static string Possessive(Case @case, Person person, Gender gender, Number number = Number.Singular){...}
 
/// <summary>
/// Притяжательные возвратные местоимения (свой, своя, свое и т.д.)
/// </summary>
public static string PossessiveReflexive(Case @case, Gender gender){...}
 
Смысл методов понятен из комментариев.
 
Метод  Personal кроме падежа, лица и рода, содержит параметр preposition (предлог) типа bool. Это необходимо, поскольку склонение личных местоимений зависит от наличия предлога. Например, третье лицо мужского рода, дательный падеж - (кому?) ему, но с предлогом - (к кому?) к нему.
 

Стеммер

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

Вспомогательные методы

Ниже приведены некоторые вспомогательные методы - расширения.
 
/// <summary>
/// Одушевленность
/// </summary>
public static Animacy Animacy(this Gender gender){...}
 
/// <summary>
/// Род без учета одушевленности
/// </summary>
public static Gender Gen(this Gender gender){...}
 
/// <summary>
/// Число без учета рода и одушеленности
/// </summary>
public static Number Number(this Gender gender){...}
 
Эти методы помогают выделять различные типы из типа Gender (который содержит в себе род, число и одушевленность). Например Animacy позволяет получить одушевленность, Number - получает число и т.д.
 

Download

Ссылка содержит LingvoNET.dll, файл документации xml и тестовый проект, в котором вы можете проверить склонения и спряжения слов. Библиотека LingvoNET бесплатна для некоммерческого использования.


 
Вопросы, связанные с библиотекой вы можете задавать в этом блоге, либо по email p_torgashov@ukr.net



 

25 комментариев:

  1. Александр23 мая 2015 г., 08:18

    Шикарная библиотека, благодарю за проделанный труд.

    ОтветитьУдалить
  2. Я использую для склонения городов, все классно, только Тюмень в предложном ТюменЕ, и Тверь в ТверЕ
    остальные города вроде правильно. Я честно говоря не знаю как заставить алгоритм различать Тюмень-Тюмени и Ярославль- Ярославле. Поэтому если будет фикс- отдельное спасибо!

    ОтветитьУдалить
    Ответы
    1. 1) Система использует грамматический словарь Зализняка как базовый. Естественно в этом словаре нет названий городов. Поэтому все города система ищет по эвристическому поиску (то есть проще говоря ищет похожие слова).
      2) Города Тюмень и Тверь система распознает как слова мужского рода, поэтому и неправильно склоняет. Исправить это достаточно просто - при вызове метода FindSimilar явно указывайте необходимый пол:
      FindSimilar("Тюмень", Gender.F);
      В таком случае склонение будет верным.

      Удалить
  3. А можно получить список всех существительных из базы?

    ОтветитьУдалить
    Ответы
    1. Можно - методом GetAll (доступен начиная с версии 1.1.2)

      Удалить
  4. К сожалению сборка не работает в .net universal apps

    ОтветитьУдалить
  5. Доброго времени. Ссылка не рабочая

    ОтветитьУдалить
    Ответы
    1. Библиотека доступна на Nuget: https://www.nuget.org/packages/LingvoNET/

      Удалить
  6. Здравствуйте!

    Интересует возможность применения данной библиотеки для коммерческого использования.

    ОтветитьУдалить
    Ответы
    1. День добрый,
      Распространяется под лицензией LGPLv3

      Удалить
  7. Этот комментарий был удален администратором блога.

    ОтветитьУдалить
  8. Здравствуйте! Для украинского языка такой библиотеки не предвидется?

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

      Удалить
  9. Здравствуйте! Не подскажите, как вытащить род существительного?
    при склонении получается, к примеру, гордой хищник, вместо гордый хищник

    ОтветитьУдалить
    Ответы
    1. Вызываете метод Nouns.FindAll("хищник") - получаете объект типа Noun. В нем есть поле Gender. Для "хищник" оно возвращает Gender.MA - то есть мужской род, одушевленное.

      Удалить
  10. Покажите, пожалуйста, пример использования LingvoNET.dll в Visual basic если это возможно.
    Библиотеку подключил, но как использовать не пойму. Например, чтоб склонять слово «Дерево».
    TextBox1.Text = LingvoNET.Verbs.FindOne("Дерево") ???

    ОтветитьУдалить
  11. Здравствуйте. я ничего не понимаю в C# и понимаю, что возможно буду отправлен "учить матчасть", но нет ли какой нибудь готовой консольной программы, чтобы она взяла исходный текстовый файл и выдала н выходе нужный со всеми склонениями и спряжениями?
    Или скажите сколько это будет стоить написать подобную программу?
    Готов обсудить.

    ОтветитьУдалить
    Ответы
    1. Тут можно - http://cyriller.2try.ws/Decline/File

      Удалить
  12. Здравствуйте, каким образом можно осуществить склонение любого веденного слова?
    Пример: Слово "Сниму", вернётся массив склонённых слов...

    ОтветитьУдалить
    Ответы
    1. День добрый,

      Библиотека для склонения слов в нормальной форме. A слово "сниму" - это не инфинитив, так что нет, нельзя.

      Но можно просто получить из библиотеки все глаголы, затем получить все словоформы и создать большой словарь всех форм всех глаголов - и в нем уже искать нужное слово. Но lingvoNET под это не заточена. Она для синтеза слов а не для анализа.

      Удалить
  13. Здравствуйте! А под .NET Core не планируется перекомпилить библиотеку?

    ОтветитьУдалить
  14. Здравствуйте. А как правильно склонять фамилии?? Например Иванов в творительном получается как Ивановом. А должно же быть Ивановым. ??

    ОтветитьУдалить