5

我正在将编写器的应用程序移植到 UWP 平台。我剩下的唯一一块拼图是 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 应用程序中运行良好。我还必须替换此功能——希望使用与拼写检查引擎相同的引擎。但是,如果您知道一个好的词库引擎(但它不进行拼写检查),那也很好!

谢谢!!

4

3 回答 3

3

我下载了 NHunspell 库的源代码,并尝试构建一个支持 UWP 的库,但是我发现 Marshalling ( Marshalling.cs )存在问题
该包加载的 dll 仅在 x86 和 x64 架构中工作,因此在 arm (mobiles,平板电脑)该应用程序将无法运行。
该包使用系统调用加载 dll:

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

我认为它需要重写才能在 UWP 中工作,因为 UWP 使用沙盒。

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

我对 UWP 的 dll 了解不多,但我相信重写可能非常困难。

于 2016-03-20T16:22:32.893 回答
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;
        }
    }
}
于 2016-03-25T14:17:23.960 回答
0

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

看看ISpellChecker。它让您可以添加自己的自定义字典,并提供更多选项来控制其行为。是的,它适用于 UWP。

于 2016-05-09T08:21:39.953 回答