我正在为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应用程序中运行良好。我也必须更换这个功能 - 希望使用与拼写检查引擎相同的引擎。但是,如果你知道一个好的同义词引擎(但它没有拼写检查),那也很好!
谢谢!
答案 0 :(得分:3)
我下载了NHunspell库的源代码,我尝试构建一个支持UWP的库,但是我发现编组问题( Marshalling.cs )有问题
该软件包加载仅在x86和x64架构中运行的dll,因此在手机(手机,平板电脑)中应用程序将无法运行。
该软件包通过系统调用加载dll:
[DllImport("kernel32.dll")]
internal static extern IntPtr LoadLibrary(string fileName);
并且我认为它需要在UWP中重写,因为UWP使用沙盒。
恕我直言,只有两个选择:我对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。