【问题标题】:C# human name checking and matching algorithmC#人名检查和匹配算法
【发布时间】:2020-02-10 13:27:29
【问题描述】:

是否有算法或 C# 库来确定人名是否正确,如果不正确,则找到最接近的匹配项?

我找到了类似 Levenshtein 距离算法的字符串匹配算法,但它们都检查一个字符串与另一个字符串之间的匹配,我想检查一个名称与所有可能的英文名称之间的匹配(例如),检查名称是否写错。

例如: 有人插入了“Giliam”这个名字,而它应该是“william”。我想知道是否有任何算法(或其中的一组)来检测错误并提出更正。

我想到的所有解决方案都涉及到一个巨大的人名字典的实现,并用它来检查每个匹配的输入名称的正确性……这对我来说听起来很可怕,所以我想寻求更好的方法。

谢谢。

【问题讨论】:

  • 人类可以被称为字面上的任何东西。坦率地说,任何“更正”名称的尝试都更有可能通过“更正”所写正确的名称而导致问题。也可能是不成比例的少数族裔和移民以这种方式得到“纠正”,这使得尝试在政治和社会上变得愚蠢,IMO。同样,切勿尝试对最少字符之类的名称进行限制,甚至不要对单独的名字/姓氏的概念进行限制。哎呀,甚至我的名字也经常被“更正”(对马克),以至于令人讨厌!
  • 你怎么知道它应该是“William”。也可以是“阿娇”
  • @OlivierJacot-Descombes 或者可能是 correct as written

标签: c# string-matching spell-checking


【解决方案1】:

您实际上要问的是如何使用给定的字典创建拼写检查器。一种不涉及查找和测试列表中每个可能条目的方法是执行问题的逆操作:从用户输入生成可能排列的列表,并测试其中的每一个以查看它们是否'在一个列表中。这是一个更易于管理的问题。

例如,您可以使用这样的函数来生成一个“编辑”可以从给定单词中获得的每个可能的排列:

static HashSet<string> GenerateEdits(string word)
{
    // Normalize the case
    word = word.ToLower();

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

    var ret = new HashSet<string>();

    // All cases of one character removed
    foreach (var cur in splits)
    {
        if (cur.Item2.Length > 0)
        {
            ret.Add(cur.Item1 + cur.Item2.Substring(1));
        }
    }

    // All transposed possibilities
    foreach (var cur in splits)
    {
        if (cur.Item2.Length > 1)
        {
            ret.Add(cur.Item1 + cur.Item2[1] + cur.Item2[0] + cur.Item2.Substring(2));
        }
    }

    var letters = "abcdefghijklmnopqrstuvwxyz";

    // All replaced characters
    foreach (var cur in splits)
    {
        if (cur.Item2.Length > 0)
        {
            foreach (var letter in letters)
            {
                ret.Add(cur.Item1 + letter + cur.Item2.Substring(1));
            }
        }
    }

    // All inserted characters
    foreach (var cur in splits)
    {
        foreach (var letter in letters)
        {
            ret.Add(cur.Item1 + letter + cur.Item2);
        }
    }

    return ret;
}

然后运行代码以查看给定的用户输入是否可以轻松地转换为这些条目之一。找到最佳匹配可以通过加权平均来完成,或者简单地通过向用户展示可能性列表来完成:

// Example file from:
// https://raw.githubusercontent.com/smashew/NameDatabases/master/NamesDatabases/first%20names/all.txt
string source = @"all.txt";
var names = new HashSet<string>();
using (var sr = new StreamReader(source))
{
    string line;
    while ((line = sr.ReadLine()) != null)
    {
        names.Add(line.ToLower());
    }
}

var userEntry = "Giliam";
var found = false;
if (names.Contains(userEntry.ToLower()))
{
    Console.WriteLine("The entered value of " + userEntry + " looks good");
    found = true;
}

if (!found)
{
    // Try edits one edit away from the user entry
    foreach (var test in GenerateEdits(userEntry))
    {
        if (names.Contains(test))
        {
            Console.WriteLine(test + " is a possibility for " + userEntry);
            found = true;
        }
    }
}

if (!found)
{
    // Try edits two edits away from the user entry
    foreach (var test in GenerateEdits(userEntry))
    {
        foreach (var test2 in GenerateEdits(test))
        {
            if (names.Contains(test))
            {
                Console.WriteLine(test + " is a possibility for " + userEntry);
                found = true;
            }
        }
    }
}
kiliam is a possibility for Giliam
liliam is a possibility for Giliam
viliam is a possibility for Giliam
wiliam is a possibility for Giliam

当然,既然您在谈论人名,您最多只能提出一个建议,并为奇怪的拼写和您从未见过的事物的拼写做好充分准备。如果您想支持其他语言,GenerateEdits 的实现会变得更加复杂,因为您考虑到“错字”的重要性

【讨论】:

    猜你喜欢
    • 2011-02-21
    • 1970-01-01
    • 1970-01-01
    • 2022-08-03
    • 2021-05-13
    • 1970-01-01
    • 2011-12-18
    • 2015-11-04
    • 1970-01-01
    相关资源
    最近更新 更多