Страницы

Поиск по вопросам

пятница, 28 февраля 2020 г.

Чем в .NET можно проверить орфографию в русском тексте?

#c_sharp #net #поиск_библиотек #проверка_орфографии


Нашел вот это, но русского к сожалению нет...

Интересуют бесплатные решения.

Вроде, как-то воспользоваться API MS Office и получать результаты, но interop тормозной.
    


Ответы

Ответ 1



Windows 8 - 10 Можно использовать стандартный Spell Checking API. Словари для проверки орфографии устанавливаются с языковым пакетом, так что при наличии русскоязычной Windows русский язык будет поддерживаться. Объявим необходимые интерфейсы: using System.Runtime.InteropServices; namespace ConsoleApplication1 { public class SpellCheckAPI { public enum WORDLIST_TYPE { WORDLIST_TYPE_IGNORE, WORDLIST_TYPE_ADD, WORDLIST_TYPE_EXCLUDE, WORDLIST_TYPE_AUTOCORRECT, } public enum CORRECTIVE_ACTION { CORRECTIVE_ACTION_NONE, CORRECTIVE_ACTION_GET_SUGGESTIONS, CORRECTIVE_ACTION_REPLACE, CORRECTIVE_ACTION_DELETE, } [Guid("B7C82D61-FBE8-4B47-9B27-6C0D2E0DE0A3")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [ComImport] public interface ISpellingError { uint StartIndex { get; } uint Length { get; } SpellCheckAPI.CORRECTIVE_ACTION CorrectiveAction { get; } string Replacement { [return: MarshalAs(UnmanagedType.LPWStr)] get; } } [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("803E3BD4-2828-4410-8290-418D1D73C762")] [ComImport] public interface IEnumSpellingError { [return: MarshalAs(UnmanagedType.Interface)] SpellCheckAPI.ISpellingError Next(); } [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("00000101-0000-0000-C000-000000000046")] [ComImport] public interface IEnumString { void Next([In] uint celt, [MarshalAs(UnmanagedType.LPWStr)] out string rgelt, out uint pceltFetched); void Skip([In] uint celt); void Reset(); void Clone([MarshalAs(UnmanagedType.Interface)] out SpellCheckAPI.IEnumString ppenum); } [Guid("432E5F85-35CF-4606-A801-6F70277E1D7A")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [ComImport] public interface IOptionDescription { string Id { [return: MarshalAs(UnmanagedType.LPWStr)] get; } string Heading { [return: MarshalAs(UnmanagedType.LPWStr)] get; } string Description { [return: MarshalAs(UnmanagedType.LPWStr)] get; } SpellCheckAPI.IEnumString Labels { [return: MarshalAs(UnmanagedType.Interface)] get; } } [Guid("0B83A5B0-792F-4EAB-9799-ACF52C5ED08A")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [ComImport] public interface ISpellCheckerChangedEventHandler { void Invoke([MarshalAs(UnmanagedType.Interface), In] SpellCheckAPI.ISpellChecker sender); } [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("B6FD0B71-E2BC-4653-8D05-F197E412770B")] [ComImport] public interface ISpellChecker { string languageTag { [return: MarshalAs(UnmanagedType.LPWStr)] get; } [return: MarshalAs(UnmanagedType.Interface)] SpellCheckAPI.IEnumSpellingError Check([MarshalAs(UnmanagedType.LPWStr), In] string text); [return: MarshalAs(UnmanagedType.Interface)] SpellCheckAPI.IEnumString Suggest([MarshalAs(UnmanagedType.LPWStr), In] string word); void Add([MarshalAs(UnmanagedType.LPWStr), In] string word); void Ignore([MarshalAs(UnmanagedType.LPWStr), In] string word); void AutoCorrect([MarshalAs(UnmanagedType.LPWStr), In] string from, [MarshalAs(UnmanagedType.LPWStr), In] string to); byte GetOptionValue([MarshalAs(UnmanagedType.LPWStr), In] string optionId); SpellCheckAPI.IEnumString OptionIds { [return: MarshalAs(UnmanagedType.Interface)] get; } string Id { [return: MarshalAs(UnmanagedType.LPWStr)] get; } string LocalizedName { [return: MarshalAs(UnmanagedType.LPWStr)] get; } uint add_SpellCheckerChanged([MarshalAs(UnmanagedType.Interface), In] SpellCheckAPI.ISpellCheckerChangedEventHandler handler); void remove_SpellCheckerChanged([In] uint eventCookie); [return: MarshalAs(UnmanagedType.Interface)] SpellCheckAPI.IOptionDescription GetOptionDescription([MarshalAs(UnmanagedType.LPWStr), In] string optionId); [return: MarshalAs(UnmanagedType.Interface)] SpellCheckAPI.IEnumSpellingError ComprehensiveCheck([MarshalAs(UnmanagedType.LPWStr), In] string text); } [Guid("8E018A9D-2415-4677-BF08-794EA61F94BB")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [ComImport] public interface ISpellCheckerFactory { SpellCheckAPI.IEnumString SupportedLanguages { [return: MarshalAs(UnmanagedType.Interface)] get; } int IsSupported([MarshalAs(UnmanagedType.LPWStr), In] string languageTag); [return: MarshalAs(UnmanagedType.Interface)] SpellCheckAPI.ISpellChecker CreateSpellChecker([MarshalAs(UnmanagedType.LPWStr), In] string languageTag); } [Guid("AA176B85-0E12-4844-8E1A-EEF1DA77F586")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [ComImport] public interface IUserDictionariesRegistrar { void RegisterUserDictionary([MarshalAs(UnmanagedType.LPWStr), In] string dictionaryPath, [MarshalAs(UnmanagedType.LPWStr), In] string languageTag); void UnregisterUserDictionary([MarshalAs(UnmanagedType.LPWStr), In] string dictionaryPath, [MarshalAs(UnmanagedType.LPWStr), In] string languageTag); } [Guid("7AB36653-1796-484B-BDFA-E74F1DB7C1DC")] [ComImport] public class SpellCheckerFactoryClass { } } } Тогда метод для проверки правописания можно реализовать так: public static string SpellCheck(string s) { SpellCheckAPI.SpellCheckerFactoryClass factory = null; SpellCheckAPI.ISpellCheckerFactory ifactory = null; SpellCheckAPI.ISpellChecker checker = null; SpellCheckAPI.ISpellingError error = null; SpellCheckAPI.IEnumSpellingError errors = null; SpellCheckAPI.IEnumString suggestions = null; StringBuilder sb = new StringBuilder(s.Length * 10); try { factory = new SpellCheckAPI.SpellCheckerFactoryClass(); ifactory = (SpellCheckAPI.ISpellCheckerFactory)factory; //проверим поддержку русского языка int res = ifactory.IsSupported("ru-RU"); if (res == 0) { throw new Exception("Fatal error: russian language not supported!"); } checker = ifactory.CreateSpellChecker("ru-RU"); errors = checker.Check(s); while (true) { if (error != null) { Marshal.ReleaseComObject(error); error = null; } error = errors.Next(); if (error == null) break; //получаем слово с ошибкой string word = s.Substring((int)error.StartIndex, (int)error.Length); sb.AppendLine("Ошибка в слове: " + word); //получаем рекомендуемое действие switch (error.CorrectiveAction) { case SpellCheckAPI.CORRECTIVE_ACTION.CORRECTIVE_ACTION_DELETE: sb.AppendLine("Рекомендуемое действие: удалить"); break; case SpellCheckAPI.CORRECTIVE_ACTION.CORRECTIVE_ACTION_REPLACE: sb.AppendLine("Рекомендуемое действие: заменить на " + error.Replacement); break; case SpellCheckAPI.CORRECTIVE_ACTION.CORRECTIVE_ACTION_GET_SUGGESTIONS: sb.AppendLine("Рекомендуемое действие: заменить на одно из следующих слов"); if (suggestions != null) { Marshal.ReleaseComObject(suggestions); suggestions = null; } //получаем список слов, предложенных для замены suggestions = checker.Suggest(word); sb.Append("[ "); while (true) { string suggestion; uint count = 0; suggestions.Next(1, out suggestion, out count); if (count == 1) sb.Append(suggestion + " "); else break; } sb.Append("] "); sb.AppendLine(); break; } sb.AppendLine(); } } finally { if (suggestions != null) { Marshal.ReleaseComObject(suggestions); } if (factory != null) { Marshal.ReleaseComObject(factory); } if (ifactory != null) { Marshal.ReleaseComObject(ifactory); } if (checker != null) { Marshal.ReleaseComObject(checker); } if (error != null) { Marshal.ReleaseComObject(error); } if (errors != null) { Marshal.ReleaseComObject(errors); } } return sb.ToString(); } Windows Vista - 7 Можно использовать TextBox из WPF, тесты показали, что он отлично работает в не-WPF проекте без необходимости создания цикла обработки сообщений и добавления его в окно. Нужно лишь добавить ссылки на PresentationCore, PresentationFramework, WindowsBase и System.Xaml, а также пометить поток STAThread. Из коробки русский язык не поддерживается, поэтому придется скачать словарь, например здесь, и добавить его как нестандартный. Файл словаря желательно перекодировать в UTF-16 LE с BOM (эта кодировка в блокноте обозначена как "Юникод"), так как словари в других кодировках похоже обрабатываются некорректно. Пример: using System.Windows.Controls; //... public static string SpellCheckWPF(string s) { StringBuilder sb = new StringBuilder(s.Length * 10); TextBox textbox = new TextBox(); textbox.Text = s; textbox.Language = System.Windows.Markup.XmlLanguage.GetLanguage("en-US"); textbox.SpellCheck.IsEnabled = true; //добавим нестандартный словарь из файла textbox.SpellCheck.CustomDictionaries.Add(new Uri(@"ru-RU.dic", UriKind.Relative)); int index = 0; while (true) { //находим ошибку index = textbox.GetNextSpellingErrorCharacterIndex(index, System.Windows.Documents.LogicalDirection.Forward); if (index > s.Length || index < 0) break; var error = textbox.GetSpellingError(index); int len = textbox.GetSpellingErrorLength(index); string word = textbox.Text.Substring(index, len); sb.AppendFormat("Ошибка в слове {0}, рекомендуется заменить на одно из следующих слов: ", word); //выводим список предлагаемых замен foreach (string x in error.Suggestions) { sb.Append(x + "; "); } sb.AppendLine(); //переход к следующему слову index += len; } return sb.ToString(); } Поскольку загрузка большого словаря занимает существенное время, нужно создать один TextBox, загрузить в него словарь один раз при запуске приложения и в последующем коде использовать его (а не грузить словарь каждый раз, когда нужно что-то проверить). Данный код не будет работать в Windows 8.1+ и .NET 4.6.1+, так как в этих версиях WPF также использует вышеописанный стандартный API. Механизм загрузки словарей в связи с этим сильно изменился, и большие словари не поддерживаются. Чтобы все нормально заработало в этом случае, нужно убрать нестандартный словарь и устанавливать в TextBox русский язык, а не английский. Источники Using the C++ Spell Checking API WPF in .NET 4.6.1 Spelling checker isn't supported in the .NET 4.6.1 in some conditions

Комментариев нет:

Отправить комментарий