在UWP应用程序中拼写库(如hunspell)?

时间:2016-03-18 00:05:01

标签: c# win-universal-app hunspell nhunspell

我正在为UWP platorm移植作家的申请。我剩下的唯一一块拼图是NHunspell图书馆。我广泛使用它进行拼写检查和同义词库功能。我已经定制了它,并为各种事物创建了自定义词典(即每个写作项目的不同字典)。这个图书馆很漂亮。

但是,我似乎无法在我的UWP应用程序中包含此DLL。

1)有没有办法强制使用这个DLL?我真的很喜欢NHunSpell系统的设置方式。它具有常识,而且非常快速且易于使用。

2)如果没有,有人可以为自定义词典,自定义拼写检查等推荐更好的解决方案吗?

更新3

经过大量更新和在线阅读后,我找到了一个讨论拼写检查理论的链接。这是一个简单的例子(我最常用的一个)。

http://www.anotherchris.net/csharp/how-to-write-a-spelling-corrector-in-csharp/

阅读完这篇文章,获取该基本代码,并从Hunspell .dic文件中删除英文单词后,我创建了自己的拼写检查库,可以在UWP中使用。

一旦我得到巩固,我会将其作为下面的答案发布,以捐赠给SO社区。 :)

更新2

我承认使用了Hunspell。看起来它根本不可能......是否还有其他人可以建议的库/包?

更新:

我可能需要重新说明我不能包含DLL的声明:我不能通过NuGet包含DLL。它抱怨DLL与UAP / UWP平台不兼容。

我能够手动通过链接到现有的DLL(不是NuGet)在我的项目中包含DLL。但是,该DLL确实证明与UAP平台不兼容。一个简单的拼写检查单词在WinForms中运行正常,但立即与System.IO.FileNotFoundException崩溃。

NHunspell的构造函数确实可以加载相关的.dic.aff文件。但是,我通过将文件加载到内存中然后调用备用构造函数来缓解这种情况,该构造函数为每个文件采用字节数组而不是文件名。它仍然崩溃,但有一个新的Method not found错误:

String System.AppDomain.get_RelativeSearchPath()

我正在寻找可在UAP框架内工作的任何拼写检查引擎。我希望它只是出于熟悉的原因而成为NHunspell。但是,我并不是因为这种情况越来越不可能作为一种选择。

与我合作的人建议我使用内置的拼写检查选项。但是,我无法使用内置的Windows 10 / TextBox拼写检查功能(我知道),因为我无法控制自定义词典,因此我无法禁用自动大写和单词替换(如果它认为它足够接近正确的猜测,它会替换你的单词)。对于作家来说,那些东西是章节自杀!作家可以在操作系统级别关闭它们,但是他们可能希望将它们用于其他应用程序,而不是这个。

如果有NHunspell的解决方法,请告诉我。如果您不知道解决方法,请告诉我您在UAP框架内运行的最佳替换自定义拼写检查引擎。

作为旁注,我还使用NHunspell作为其词库功能。它在我的Windows应用程序中运行良好。我也必须更换这个功能 - 希望使用与拼写检查引擎相同的引擎。但是,如果你知道一个好的同义词引擎(但它没有拼写检查),那也很好!

谢谢!

3 个答案:

答案 0 :(得分:3)

我下载了NHunspell库的源代码,我尝试构建一个支持UWP的库,但是我发现编组问题( Marshalling.cs )有问题 该软件包加载仅在x86和x64架构中运行的dll,因此在手机(手机,平板电脑)中应用程序将无法运行。
该软件包通过系统调用加载dll:

    [DllImport("kernel32.dll")]
    internal static extern IntPtr LoadLibrary(string fileName);

并且我认为它需要在UWP中重写,因为UWP使用沙盒。

恕我直言,只有两个选择:
1)用UWP的限制重写编组类 2)不要在你的程序中使用Hunspell。

我对UWP的dll知之甚少,但我相信重写可能非常困难。

答案 1 :(得分:1)

正如所承诺的,这是我为进行拼写检查而建立的课程。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Com.HanelDev.HSpell
{
    public class HSpellProcess
    {
        private Dictionary<string, string> _dictionary = new Dictionary<string, string>();

        public int MaxSuggestionResponses { get; set; }

        public HSpellProcess()
        {
            MaxSuggestionResponses = 10;
        }

        public void AddToDictionary(string w)
        {
            if (!_dictionary.ContainsKey(w.ToLower()))
            {
                _dictionary.Add(w.ToLower(), w);
            }
            else
            {
                // Upper case words are more specific (but may be the first word
                // in a sentence.) Lower case words are more generic.
                // If you put an upper-case word in the dictionary, then for
                // it to be "correct" it must match case. This is not true
                // for lower-case words.
                // We want to only replace existing words with their more
                // generic versions, not the other way around.
                if (_dictionary[w.ToLower()].CaseSensitive())
                {
                    _dictionary[w.ToLower()] = w;
                }
            }
        }

        public void LoadDictionary(byte[] dictionaryFile, bool resetDictionary = false)
        {
            if (resetDictionary)
            {
                _dictionary = new Dictionary<string, string>();
            }
            using (MemoryStream ms = new MemoryStream(dictionaryFile))
            {
                using (StreamReader sr = new StreamReader(ms))
                {
                    string tmp = sr.ReadToEnd();
                    tmp = tmp.Replace("\r\n", "\r").Replace("\n", "\r");
                    string [] fileData = tmp.Split("\r".ToCharArray());

                    foreach (string line in fileData)
                    {
                        if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#"))
                        {
                            continue;
                        }

                        string word = line;

                        // I added all of this for file imports (not array imports)
                        // to be able to handle words from Hunspell dictionaries.
                        // I don't get the hunspell derivatives, but at least I get
                        // the root word.
                        if (line.Contains("/"))
                        {
                            string[] arr = line.Split("/".ToCharArray());
                            word = arr[0];
                        }

                        AddToDictionary(word);
                    }
                }
            }
        }

        public void LoadDictionary(Stream dictionaryFileStream, bool resetDictionary = false)
        {
            string s = "";
            using (StreamReader sr = new StreamReader(dictionaryFileStream))
            {
                s = sr.ReadToEnd();
            }

            byte [] bytes = Encoding.UTF8.GetBytes(s);

            LoadDictionary(bytes, resetDictionary);
        }

        public void LoadDictionary(List<string> words, bool resetDictionary = false)
        {
            if (resetDictionary)
            {
                _dictionary = new Dictionary<string, string>();
            }

            foreach (string line in words)
            {
                if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#"))
                {
                    continue;
                }

                AddToDictionary(line);
            }
        }

        public string ExportDictionary()
        {
            StringBuilder sb = new StringBuilder();

            foreach (string k in _dictionary.Keys)
            {
                sb.AppendLine(_dictionary[k]);
            }

            return sb.ToString();
        }

        public HSpellCorrections Correct(string word)
        {
            HSpellCorrections ret = new HSpellCorrections();
            ret.Word = word;

            if (_dictionary.ContainsKey(word.ToLower()))
            {
                string testWord = word;
                string dictWord = _dictionary[word.ToLower()];
                if (!dictWord.CaseSensitive())
                {
                    testWord = testWord.ToLower();
                    dictWord = dictWord.ToLower();
                }

                if (testWord == dictWord)
                {
                    ret.SpelledCorrectly = true;
                    return ret;
                }
            }

            // At this point, we know the word is assumed to be spelled incorrectly. 
            // Go get word candidates.
            ret.SpelledCorrectly = false;

            Dictionary<string, HSpellWord> candidates = new Dictionary<string, HSpellWord>();

            List<string> edits = Edits(word);

            GetCandidates(candidates, edits);

            if (candidates.Count > 0)
            {
                return BuildCandidates(ret, candidates);
            }

            // If we didn't find any candidates by the main word, look for second-level candidates based on the original edits.
            foreach (string item in edits)
            {
                List<string> round2Edits = Edits(item);

                GetCandidates(candidates, round2Edits);
            }

            if (candidates.Count > 0)
            {
                return BuildCandidates(ret, candidates);
            }

            return ret;
        }

        private void GetCandidates(Dictionary<string, HSpellWord> candidates, List<string> edits)
        {
            foreach (string wordVariation in edits)
            {
                if (_dictionary.ContainsKey(wordVariation.ToLower()) &&
                    !candidates.ContainsKey(wordVariation.ToLower()))
                {
                    HSpellWord suggestion = new HSpellWord(_dictionary[wordVariation.ToLower()]);

                    suggestion.RelativeMatch = RelativeMatch.Compute(wordVariation, suggestion.Word);

                    candidates.Add(wordVariation.ToLower(), suggestion);
                }
            }
        }

        private HSpellCorrections BuildCandidates(HSpellCorrections ret, Dictionary<string, HSpellWord> candidates)
        {
            var suggestions = candidates.OrderByDescending(c => c.Value.RelativeMatch);

            int x = 0;

            ret.Suggestions.Clear();
            foreach (var suggest in suggestions)
            {
                x++;
                ret.Suggestions.Add(suggest.Value.Word);

                // only suggest the first X words.
                if (x >= MaxSuggestionResponses)
                {
                    break;
                }
            }

            return ret;
        }

        private List<string> Edits(string word)
        {
            var splits = new List<Tuple<string, string>>();
            var transposes = new List<string>();
            var deletes = new List<string>();
            var replaces = new List<string>();
            var inserts = new List<string>();

            // Splits
            for (int i = 0; i < word.Length; i++)
            {
                var tuple = new Tuple<string, string>(word.Substring(0, i), word.Substring(i));
                splits.Add(tuple);
            }

            // Deletes
            for (int i = 0; i < splits.Count; i++)
            {
                string a = splits[i].Item1;
                string b = splits[i].Item2;
                if (!string.IsNullOrEmpty(b))
                {
                    deletes.Add(a + b.Substring(1));
                }
            }

            // Transposes
            for (int i = 0; i < splits.Count; i++)
            {
                string a = splits[i].Item1;
                string b = splits[i].Item2;
                if (b.Length > 1)
                {
                    transposes.Add(a + b[1] + b[0] + b.Substring(2));
                }
            }

            // Replaces
            for (int i = 0; i < splits.Count; i++)
            {
                string a = splits[i].Item1;
                string b = splits[i].Item2;
                if (!string.IsNullOrEmpty(b))
                {
                    for (char c = 'a'; c <= 'z'; c++)
                    {
                        replaces.Add(a + c + b.Substring(1));
                    }
                }
            }

            // Inserts
            for (int i = 0; i < splits.Count; i++)
            {
                string a = splits[i].Item1;
                string b = splits[i].Item2;
                for (char c = 'a'; c <= 'z'; c++)
                {
                    inserts.Add(a + c + b);
                }
            }

            return deletes.Union(transposes).Union(replaces).Union(inserts).ToList();
        }

        public HSpellCorrections CorrectFrom(string txt, int idx)
        {
            if (idx >= txt.Length)
            {
                return null;
            }

            // Find the next incorrect word.
            string substr = txt.Substring(idx);
            int idx2 = idx;

            List<string> str = substr.Split(StringExtensions.WordDelimiters).ToList();

            foreach (string word in str)
            {
                string tmpWord = word;

                if (string.IsNullOrEmpty(word))
                {
                    idx2++;
                    continue;
                }

                // If we have possessive version of things, strip the 's off before testing
                // the word. THis will solve issues like "My [mother's] favorite ring."
                if (tmpWord.EndsWith("'s"))
                {
                    tmpWord = word.Substring(0, tmpWord.Length - 2);
                }

                // Skip things like ***, #HashTagsThatMakeNoSense and 1,2345.67
                if (!tmpWord.IsWord())
                {
                    idx2 += word.Length + 1;
                    continue;
                }

                HSpellCorrections cor = Correct(tmpWord);

                if (cor.SpelledCorrectly)
                {
                    idx2 += word.Length + 1;
                }
                else
                {
                    cor.Index = idx2;
                    return cor;
                }
            }

            return null;
        }
    }
}

答案 2 :(得分:0)

您可以直接使用Windows内置拼写检查程序,以便更好地控制它的行为。然后自己将结果应用到文本框控件。

看看ISpellChecker。它让你add自己的自定义词典,并有更多的选项来控制它的行为。是的,它可用于UWP。