将人名称解析为组成部分的简单方法?

时间:2008-09-19 16:23:29

标签: .net vb.net string

很多联系人管理程序都这样做 - 你输入一个名字(例如,“John W. Smith”),它会自动将其分解为:

名字:约翰
中间名: W.
姓氏:史密斯

同样地,它会找出像“简·史密斯夫人”和“约翰·多伊博士”这样的事情。也正确(假设您允许在名称中使用“prefix”和“suffix”等字段)。

我认为这是人们想要做的相当普遍的事情......所以问题是......你会怎么做?是否有简单的算法?也许正则表达式?

我正在使用.NET解决方案,但我并不挑剔。

更新:我很欣赏没有简单的解决方案来涵盖所有边缘案例和文化......但是为了论证的缘故,你需要将这个名称分成几部分(填写完整版)表格 - 例如,税收或其他政府表格 - 是一种情况,您必须将名称输入固定字段,无论您是否喜欢),但您不一定要强迫用户输入将名称命名为离散字段(减少输入=新手用户更容易)。

你想让程序“猜测”(尽可能最好)在第一个,中间的,最后一个等等。如果可以的话,看看Microsoft Outlook如何为联系人做这个 - 它让你输入名称,但如果您需要澄清,可以打开一个额外的小窗口。我会做同样的事情 - 给用户提供窗口,以防他们想要以不连续的部分输入名称 - 但允许在一个框中输入名称并做一个涵盖大多数的“最佳猜测”俗名。

23 个答案:

答案 0 :(得分:32)

如果您必须进行此解析,我相信您会在此处获得很多好建议。

我的建议是 - 不要进行此解析

而是创建输入字段,以便信息已经分离出来。有标题,名字,中间名,姓氏,后缀等单独的字段。

答案 1 :(得分:17)

我知道这已经过时了,可能已经找到了我无法找到的地方的答案,但由于我找不到任何适合我的地方,这就是我想出来的,我觉得它很像谷歌联系人和Microsoft Outlook。它不能很好地处理边缘情况,但对于一个好的CRM类型的应用程序,总是可以要求用户解决这些问题(在我的应用程序中我实际上一直有单独的字段,但我需要这个来从另一个应用程序导入数据只有一个字段):

    public static void ParseName(this string s, out string prefix, out string first, out string middle, out string last, out string suffix)
    {
        prefix = "";
        first = "";
        middle = "";
        last = "";
        suffix = "";

        // Split on period, commas or spaces, but don't remove from results.
        List<string> parts = Regex.Split(s, @"(?<=[., ])").ToList();

        // Remove any empty parts
        for (int x = parts.Count - 1; x >= 0; x--)
            if (parts[x].Trim() == "")
                parts.RemoveAt(x);

        if (parts.Count > 0)
        {
            // Might want to add more to this list
            string[] prefixes = { "mr", "mrs", "ms", "dr", "miss", "sir", "madam", "mayor", "president" };

            // If first part is a prefix, set prefix and remove part
            string normalizedPart = parts.First().Replace(".", "").Replace(",", "").Trim().ToLower();
            if (prefixes.Contains(normalizedPart))
            {
                prefix = parts[0].Trim();
                parts.RemoveAt(0);
            }
        }

        if (parts.Count > 0)
        {
            // Might want to add more to this list, or use code/regex for roman-numeral detection
            string[] suffixes = { "jr", "sr", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x", "xi", "xii", "xiii", "xiv", "xv" };

            // If last part is a suffix, set suffix and remove part
            string normalizedPart = parts.Last().Replace(".", "").Replace(",", "").Trim().ToLower();
            if (suffixes.Contains(normalizedPart))
            {
                suffix = parts.Last().Replace(",", "").Trim();
                parts.RemoveAt(parts.Count - 1);
            }
        }

        // Done, if no more parts
        if (parts.Count == 0)
            return;

        // If only one part left...
        if (parts.Count == 1)
        {
            // If no prefix, assume first name, otherwise last
            // i.e.- "Dr Jones", "Ms Jones" -- likely to be last
            if(prefix == "")
                first = parts.First().Replace(",", "").Trim();
            else
                last = parts.First().Replace(",", "").Trim();
        }

        // If first part ends with a comma, assume format:
        //   Last, First [...First...]
        else if (parts.First().EndsWith(","))
        {
            last = parts.First().Replace(",", "").Trim();
            for (int x = 1; x < parts.Count; x++)
                first += parts[x].Replace(",", "").Trim() + " ";
            first = first.Trim();
        }

        // Otherwise assume format:
        // First [...Middle...] Last

        else
        {
            first = parts.First().Replace(",", "").Trim();
            last = parts.Last().Replace(",", "").Trim();
            for (int x = 1; x < parts.Count - 1; x++)
                middle += parts[x].Replace(",", "").Trim() + " ";
            middle = middle.Trim();
        }
    }

很抱歉,代码很长很难看,我还没有去清理它。它是一个C#扩展,所以你可以使用它:

string name = "Miss Jessica Dark-Angel Alba";
string prefix, first, middle, last, suffix;
name.ParseName(out prefix, out first, out middle, out last, out suffix);

答案 2 :(得分:10)

没有简单的解决方案。名称构造因文化而异,甚至在英语世界中,前缀和后缀也不一定是名称的一部分。

一种基本的方法是在字符串的开头寻找敬语(例如,“Hon.John Doe”)和最后的数字或其他字符串(例如,“John Doe IV”,“John Doe Jr. “),但实际上你所能做的只是运用一套启发式方法,并希望最好。

查找未处理名称列表并根据它测试算法可能很有用。我不知道那里有什么预先包装好的。

答案 3 :(得分:5)

你可能不需要做任何奇特的事情。这样的事情应该有效。

    Name = Name.Trim();

    arrNames = Name.Split(' ');

    if (arrNames.Length > 0) {
        GivenName = arrNames[0];
    }
    if (arrNames.Length > 1) {
        FamilyName = arrNames[arrNames.Length - 1];
    }
    if (arrNames.Length > 2) {
        MiddleName = string.Join(" ", arrNames, 1, arrNames.Length - 2);
    }

您可能还想先检查标题。

答案 4 :(得分:4)

我必须这样做。实际上,比这更难的事情,因为有时候“名字”会是“史密斯,约翰”或“史密斯约翰”而不是“约翰史密斯”,或者根本不是一个人的名字,而是一个公司的名字。并且它必须自动执行,用户无法更正它。

我最终做的是提出一个名称可能包含的有限模式列表,例如:
最后,第一个中间初始 第一个最后一个 第一个中期 - 最后一个 最后,第一中 第一个中期最后一个 最后一次

也请你的先生,小Jr。假设你最终得到了十几种模式。

我的应用程序有一个常用名字的字典,常用的姓氏(你可以在网上找到这些),常见的标题,常见的后缀(jr,sr,md)和使用它可以做出真正好的猜测模式。我不是那么聪明,我的逻辑并不那么花哨,但是,创造一些在99%以上的时间里猜对了的逻辑并不难。

答案 5 :(得分:3)

我很欣赏正确很难做到 - 但是如果你为用户提供了一种编辑结果的方法(例如,如果没有猜到,可以使用弹出窗口编辑名称对大多数情况来说仍然猜对了“正确”......当然这是猜测很难。

从理论上看问题时,很容易说“不要这样做”,但有时情况会另有规定。拥有名称的所有部分(标题,第一,中间,后缀,后缀,仅举几例)的字段可占用大量屏幕空间 - 并结合地址问题(另一天的主题) )可以真正搞乱应该是一个干净,简单的用户界面。

我想答案应该是“除非你必须这样做,否则不要这样做,如果你这样做,请保持简单(这里已经发布了一些方法)并为用户提供编辑结果的方法需要“。

答案 6 :(得分:3)

理解这是一个坏主意,我在perl中写了这个正则表达式 - 这对我来说是最好的。我已经过滤了公司名称 以vcard格式输出:(hon_prefix,given_name,additional_name,family_name,hon.suffix)

/^ \s*
    (?:((?:Dr.)|(?:Mr.)|(?:Mr?s.)|(?:Miss)|(?:2nd\sLt.)|(?:Sen\.?))\s+)? # prefix
    ((?:\w+)|(?:\w\.)) # first name
(?: \s+ ((?:\w\.?)|(?:\w\w+)) )?  # middle initial
(?: \s+ ((?:[OD]['’]\s?)?[-\w]+))    # last name
(?: ,? \s+ ( (?:[JS]r\.?) | (?:Esq\.?) | (?: (?:M)|(?:Ph)|(?:Ed) \.?\s*D\.?) | 
         (?: R\.?N\.?) | (?: I+) )  )? # suffix
\s* $/x

备注:

  • 不处理IV,V,VI
  • 前缀,后缀的硬编码列表。从~2K名称的数据集演变而来
  • 不处理多个后缀(例如,MD,PhD)
  • 专为美国名字设计 - 无法在罗马化日语名称或其他命名系统上正常使用

答案 7 :(得分:2)

已经进行了10年的对话,但仍在寻找一种优雅的解决方案,我通读了该线程,并决定采用@eselk采取的方法,但请继续进行下去:

public class FullNameDTO
{
    public string Prefix     { get; set; }
    public string FirstName  { get; set; }
    public string MiddleName { get; set; }
    public string LastName   { get; set; }
    public string Suffix     { get; set; }
}

public static class FullName
{
    public static FullNameDTO GetFullNameDto(string fullName)
    {
        string[] knownPrefixes    = { "mr", "mrs", "ms", "miss", "dr", "sir", "madam", "master", "fr", "rev", "atty", "hon", "prof", "pres", "vp", "gov", "ofc" };
        string[] knownSuffixes    = { "jr", "sr", "ii", "iii", "iv", "v", "esq", "cpa", "dc", "dds", "vm", "jd", "md", "phd" };
        string[] lastNamePrefixes = { "da", "de", "del", "dos", "el", "la", "st", "van", "von" };

        var prefix     = string.Empty;
        var firstName  = string.Empty;
        var middleName = string.Empty;
        var lastName   = string.Empty;
        var suffix     = string.Empty;

        var fullNameDto = new FullNameDTO
        {
            Prefix     = prefix,
            FirstName  = firstName,
            MiddleName = middleName,
            LastName   = lastName,
            Suffix     = suffix
        };

        // Split on period, commas or spaces, but don't remove from results.
        var namePartsList = Regex.Split(fullName, "(?<=[., ])").ToList();

        #region Clean out the crap.
        for (var x = namePartsList.Count - 1; x >= 0; x--)
        {
            if (namePartsList[x].Trim() == string.Empty)
            {
                namePartsList.RemoveAt(x);
            }
        }
        #endregion

        #region Trim all of the parts in the list
        for (var x = namePartsList.Count - 1; x >= 0; x--)
        {
            namePartsList[x] = namePartsList[x].Trim();
        }
        #endregion

        #region Only one Name Part - assume a name like "Cher"
        if (namePartsList.Count == 1)
        {
            firstName = namePartsList.First().Replace(",", string.Empty).Trim();
            fullNameDto.FirstName = firstName;

            namePartsList.RemoveAt(0);
        }
        #endregion

        #region Get the Prefix
        if (namePartsList.Count > 0)
        {
            //If we find a prefix, save it and drop it from the overall parts
            var cleanedPart = namePartsList.First()
                                           .Replace(".", string.Empty)
                                           .Replace(",", string.Empty)
                                           .Trim()
                                           .ToLower();

            if (knownPrefixes.Contains(cleanedPart))
            {
                prefix = namePartsList[0].Trim();
                fullNameDto.Prefix = prefix;

                namePartsList.RemoveAt(0);
            }
        }
        #endregion

        #region Get the Suffix
        if (namePartsList.Count > 0)
        {
            #region Scan the full parts list for a potential Suffix
            foreach (var namePart in namePartsList)
            {
                var cleanedPart = namePart.Replace(",", string.Empty)
                                          .Trim()
                                          .ToLower();

                if (!knownSuffixes.Contains(cleanedPart.Replace(".", string.Empty))) { continue; }

                if (namePart.ToLower() == "jr" && namePart != namePartsList.Last()) { continue; }

                suffix             = namePart.Replace(",", string.Empty).Trim();
                fullNameDto.Suffix = suffix;

                namePartsList.Remove(namePart);
                break;
            }
            #endregion
        }
        #endregion

        //If, strangely, there's nothing else in the overall parts... we're done here.
        if (namePartsList.Count == 0) { return fullNameDto; }

        #region Prefix/Suffix taken care of - only one "part" left.
        if (namePartsList.Count == 1)
        {
            //If no prefix, assume first name (e.g. "Cher"), otherwise last (e.g. "Dr Jones", "Ms Jones")
            if (prefix == string.Empty)
            {
                firstName = namePartsList.First().Replace(",", string.Empty).Trim();
                fullNameDto.FirstName = firstName;
            }
            else
            {
                lastName = namePartsList.First().Replace(",", string.Empty).Trim();
                fullNameDto.LastName = lastName;
            }
        }
        #endregion

        #region First part ends with a comma
        else if (namePartsList.First().EndsWith(",") || (namePartsList.Count >= 3 && namePartsList.Any(n => n == ",") && namePartsList.Last() != ","))
        {
            #region Assume format: "Last, First"
            if (namePartsList.First().EndsWith(","))
            {
                lastName             = namePartsList.First().Replace(",", string.Empty).Trim();
                fullNameDto.LastName = lastName;
                namePartsList.Remove(namePartsList.First());

                firstName             = namePartsList.First();
                fullNameDto.FirstName = firstName;
                namePartsList.Remove(namePartsList.First());

                if (!namePartsList.Any()) { return fullNameDto; }

                foreach (var namePart in namePartsList)
                {
                    middleName += namePart.Trim() + " ";
                }
                fullNameDto.MiddleName = middleName;

                return fullNameDto;
            }
            #endregion

            #region Assume strange scenario like "Last Suffix, First"
            var indexOfComma = namePartsList.IndexOf(",");

            #region Last Name is the first thing in the list
            if (indexOfComma == 1)
            {
                namePartsList.Remove(namePartsList[indexOfComma]);

                lastName             = namePartsList.First().Replace(",", string.Empty).Trim();
                fullNameDto.LastName = lastName;
                namePartsList.Remove(namePartsList.First());

                firstName             = namePartsList.First();
                fullNameDto.FirstName = firstName;
                namePartsList.Remove(namePartsList.First());

                if (!namePartsList.Any()) { return fullNameDto; }

                foreach (var namePart in namePartsList)
                {
                    middleName += namePart.Trim() + " ";
                }
                fullNameDto.MiddleName = middleName;

                return fullNameDto;
            }
            #endregion

            #region Last Name might be a prefixed one, like "da Vinci"
            if (indexOfComma == 2)
            {
                var possibleLastPrefix = namePartsList.First()
                                                      .Replace(".", string.Empty)
                                                      .Replace(",", string.Empty)
                                                      .Trim()
                                                      .ToLower();

                if (lastNamePrefixes.Contains(possibleLastPrefix))
                {
                    namePartsList.Remove(namePartsList[indexOfComma]);

                    var lastPrefix = namePartsList.First().Trim();
                    namePartsList.Remove(lastPrefix);

                    lastName             = $"{lastPrefix} {namePartsList.First().Replace(",", string.Empty).Trim()}";
                    fullNameDto.LastName = lastName;
                    namePartsList.Remove(namePartsList.First());
                }
                else
                {
                    lastName = namePartsList.First().Replace(",", string.Empty).Trim();
                    namePartsList.Remove(namePartsList.First());

                    lastName = lastName + " " + namePartsList.First().Replace(",", string.Empty).Trim();
                    namePartsList.Remove(namePartsList.First());

                    fullNameDto.LastName = lastName;
                }

                namePartsList.Remove(",");

                firstName             = namePartsList.First();
                fullNameDto.FirstName = firstName;
                namePartsList.Remove(namePartsList.First());

                if (!namePartsList.Any()) { return fullNameDto; }

                foreach (var namePart in namePartsList)
                {
                    middleName += namePart.Trim() + " ";
                }
                fullNameDto.MiddleName = middleName;

                return fullNameDto;
            }
            #endregion
            #endregion
        }
        #endregion

        #region Everything else
        else
        {
            if (namePartsList.Count >= 3)
            {
                firstName = namePartsList.First().Replace(",", string.Empty).Trim();
                fullNameDto.FirstName = firstName;
                namePartsList.RemoveAt(0);

                //Check for possible last name prefix

                var possibleLastPrefix = namePartsList[namePartsList.Count - 2]
                                               .Replace(".", string.Empty)
                                               .Replace(",", string.Empty)
                                               .Trim()
                                               .ToLower();

                if (lastNamePrefixes.Contains(possibleLastPrefix))
                {
                    lastName = $"{namePartsList[namePartsList.Count - 2].Trim()} {namePartsList[namePartsList.Count -1].Replace(",", string.Empty).Trim()}";
                    fullNameDto.LastName = lastName;

                    namePartsList.RemoveAt(namePartsList.Count - 1);
                    namePartsList.RemoveAt(namePartsList.Count - 1);
                }
                else
                {
                    lastName = namePartsList.Last().Replace(",", string.Empty).Trim();
                    fullNameDto.LastName = lastName;

                    namePartsList.RemoveAt(namePartsList.Count - 1);
                }

                middleName = string.Join(" ", namePartsList).Trim();
                fullNameDto.MiddleName = middleName;

                namePartsList.Clear();
            }
            else
            {
                if (namePartsList.Count == 1)
                {
                    lastName = namePartsList.First().Replace(",", string.Empty).Trim();
                    fullNameDto.LastName = lastName;

                    namePartsList.RemoveAt(0);
                }
                else
                {
                    var possibleLastPrefix = namePartsList.First()
                                             .Replace(".", string.Empty)
                                             .Replace(",", string.Empty)
                                             .Trim()
                                             .ToLower();

                    if (lastNamePrefixes.Contains(possibleLastPrefix))
                    {
                        lastName = $"{namePartsList.First().Replace(",", string.Empty).Trim()} {namePartsList.Last().Replace(",", string.Empty).Trim()}";
                        fullNameDto.LastName = lastName;

                        namePartsList.Clear();
                    }
                    else
                    {
                        firstName = namePartsList.First().Replace(",", string.Empty).Trim();
                        fullNameDto.FirstName = firstName;

                        namePartsList.RemoveAt(0);

                        lastName = namePartsList.Last().Replace(",", string.Empty).Trim();
                        fullNameDto.LastName = lastName;

                        namePartsList.Clear();
                    }
                }
            }
        }
        #endregion

        namePartsList.Clear();

        fullNameDto.Prefix     = prefix;
        fullNameDto.FirstName  = firstName;
        fullNameDto.MiddleName = middleName;
        fullNameDto.LastName   = lastName;
        fullNameDto.Suffix     = suffix;

        return fullNameDto;
    }
}

这将处理很多不同的情况,为了确保这一点,我已经写出(到目前为止)超过50种不同的单元测试。

再次向@eselk提出他的想法,这些想法对我撰写他的出色解决方案的扩展版本有所帮助。而且,作为奖励,它还可以处理一个名为“ JR”的人的奇怪实例。

答案 8 :(得分:2)

这里真正的解决方案不回答这个问题。必须遵守信息的预示。名字不仅仅是一个名字;这就是我们所知道的。

这里的问题不是确切地知道什么部分是什么,以及它们用于什么。尊敬的前缀只应在个人的反对意见中授予;医生是一种来自头衔的敬意。关于一个人的所有信息都与他们的身份相关,它只是确定什么是相关信息。由于管理原因,您需要名字和姓氏;电话号码,电子邮件地址,土地描述和邮寄地址;一切都要认同,知道你在与谁打交道。

这里真正的问题是这个人迷失在政府中。突然之后,只有将个人信息输入表格并将其提交给任意程序进行处理后,他们才能获得各种各样的敬意和祝福,并通过预制模板进行展示。这是错的;尊敬的先生或女士,如果出现个人利益的原因,那么就不应该从模板上写下一封信。个人腐败需要一点关于收件人的知识。男性或女性,去学校做医生或法官,他们养成了什么文化。

在其他文化中,名称由可变数量的字符组成。我们这个人的名字只能被解释为一串数字,其中空格实际上是由字符宽度而不是空格字符决定的。在这些情况下的荣誉是一个或多个字符前缀和后缀实际名称。有礼貌的事情就是使用你给出的字符串,如果你知道尊敬,那么一定要使用它,但这再次暗示了对收件人的某种个人知识。除了Sensei之外的其他任何东西都是错误的。不是出现逻辑错误,而是因为你刚刚侮辱了你的来电者,现在你应该找到一个可以帮助你道歉的模板。

出于自动化,非个人化的相对论的目的,可以为日常文章,每周问题或其他任何事项设计模板,但是当收件人向自动服务发起反贪时,问题变得很重要。

错误发生了什么。丢失的信息。未知或缺少的信息将始终生成异常。真正的问题不在于你如何用一个表达式将一个人的名字分成单独的组成部分,而是你怎么称呼他们。

解决方案是创建一个额外的字段,如果已经有名字和名字,则将其设为可选字段,然后将其命名为“#34;我们可以称之为”#34;或&#34;我们应该如何将您称为&#34;。医生和法官将确保您妥善处理这些问题。这些不是编程问题,而是沟通问题。

好的,不好的方式,但在我看来,用户名,标记名和ID更糟糕。 所以我的解决方案是缺少的问题,&#34;我们应该怎么称呼你?&#34;

这只是一个可以提出新问题的解决方案。 Tact占上风。 在用户表单上创建一个新字段,称之为别名,为用户标记&#34;我们应该给您打电话什么?&#34;然后您就有了与之通信的方式。使用名字和姓氏,除非收件人已给出别名,或亲自熟悉发件人,然后可以接受第一名和中名。

To Me, _______________________ (standard subscribed corrospondence)
To Me ( Myself | I ), ________ (standard recipient instigated corrospondence)
To Me Myself I, ______________ (look out, its your mother, and you're in big trouble;
                                nobody addresses a person by their actual full name)

Dear *(Mr./Mrs./Ms./Dr./Hon./Sen.) Me M. I *(I),
To Whom it may Concern;

否则你正在寻找标准的东西:你好,问候,你可能是胜利者。

如果您在一个字符串中包含一个人姓名的数据,那么您就不会有问题,因为您已经拥有了他们的别名。如果您需要的是名字和姓氏,那么只需要左(姓名,instr(姓名,&#34;&#34;))&amp; &#34; &#34; &安培;对(姓名,instrrev(姓名,&#34;&#34;)),我的数学可能是错的,我有点不合时宜。用左右比较已知的前缀和后缀,并从匹配中删除它们。通常,除了确认身份的情况外,很少使用中间名;哪个地址或电话号码会告诉您更多信息。观察是否发生了变形,可以确定如果不使用姓氏,那么其中一个中间名就是。

为了搜索名字和姓氏的列表,必须考虑使用其中一个中间名称的可能性;这将需要四次搜索:一次搜索第一个&amp;最后,然后另一个过滤第一个&amp;中间,然后另一个过滤中间和最后,然后另一个过滤中间&amp;中间。最终,名字始终是第一个,而最后一个名字总是最后一个,并且可以有任意数量的中间名;少即是多,零可能,但不太可能。

有时人们更喜欢被称为比尔,哈利,吉姆,鲍勃,道格,贝丝,苏或麦当娜;比他们的实际名字;类似但不切实际地期望任何人都能理解所有不同的可能性。

你能做的最有礼貌的事情就是问;我们能打电话给你什么?

答案 9 :(得分:2)

我们在公司中使用了一些插件来实现这一目标。我最终创建了一种方法来为不同的客户端在不同的导入上实际指定名称的格式。有一家公司有一个工具,根据我的经验非常物有所值,在处理这个问题时真的很不可思议。它位于:http://www.softwarecompany.com/,效果很好。使用任何统计方法执行此操作的最有效方法是使用逗号或空格分割字符串:1。剥离标题和前缀2.条带后缀3,解析名称顺序为(2个名称= F &amp; L,3个名字= FML或LMF),取决于string()的顺序。

答案 10 :(得分:1)

如果您只是必须这样做,请将猜测作为可选选项添加到UI。这样,您可以告诉用户您如何解析名称,并让他们从您提供的列表中选择不同的解析。

答案 11 :(得分:1)

我已经在页面加载时执行此服务器端。写了一个Coldfusion CFC,它传递了两个参数 - 实际的用户数据(名字,中间名,姓)和数据类型(第一个,中间的,最后一个)。然后例行检查连字符,撇号,空格和格式。恩。 MacDonald,McMurray,O'Neill,Rodham-Clinton,Eric von Dutch,G。W. Bush,Jack Burton Jr.,Paul Okin博士,Chris di Santos。对于用户只有一个名称的情况,只需要第一个名称字段,中间名和姓氏是可选的。

所有信息都以小写形式存储 - 除了前缀,后缀和自定义。此格式化在页面渲染上完成,而不是在存储到数据库期间。虽然用户输入数据时存在验证过滤。对不起,无法发布代码。开始使用正则表达式,但对于所有场景都变得过于混乱和不可靠。使用标准逻辑块(if / else,switch / case),更易于阅读和调试。让每个输入/数据库字段分离!是的,这将需要一些编码,但在完成后它应该占99%的组合。到目前为止,仅基于英文名称,没有国际化,这是另一个蜡烛。

以下是需要考虑的事项:

  • Hypens(例如Rodham-Clinton,可能在第一,中间或最后)
  • Apostrophes(例如O'Neill,可能在第一,中间或最后)
  • 空间
  • Mc和Mac(例如麦当劳,MacMurray,可以是第一,中间或者第一 最后)
  • 名字:多个名字(例如Joe Bob Briggs)
  • 姓氏:de,di,et,der,den,van,von,af应为小写(ex Eric von Dander,Mary di Carlo)
  • 前缀:Dr.,Prof。等
  • 后缀:Jr.,Sr。,Esq。,II,III等

当用户输入info时,db中的字段模式如下:

  • 前缀/标题(Dr.,等使用下拉列表)
  • 前缀/标题自定义(用户可以使用文本输入自定义,例如上尉 ())
  • 名字
  • 中间
  • 姓氏
  • 后缀(Jr.,III,Prof。,Ret。等,使用下拉列表)
  • 后缀自定义(用户可以输入自定义,例如每次转化费用)

这是我用来制作每个名字大写的第一个字母的正则表达式。我首先运行它,然后根据规则遵循例程格式(它是Coldfusion格式,但你明白了):

<cfset var nameString = REReplace(LCase(nameString), "(^[[:alpha:]]|[[:blank:]][[:alpha:]])", "\U\1\E", "ALL")>

你也可以使用JavaScript和CSS来做客户端 - 甚至可能更容易 - 但我更喜欢做服务器端,因为我需要在页面加载客户端之前设置变量。

答案 12 :(得分:1)

有一种称为NetGender的第三方工具,效果出奇的好。我使用它以不可预测的格式解析大量真正错误形成的名称。看看他们页面上的示例,您也可以下载并试用它。

http://www.softwarecompany.com/dotnet/netgender.htm

我根据420万个名字的样本得出了这些统计数据。名称部件是指以空格分隔的不同部件的数量。对于数据库中的大多数名称,非常高的百分比是正确的。随着部件的增加,正确性下降了,但很少有名称包含&gt; 3个零件,而更少的名称包含&gt; 4。这对我们的情况来说已经足够了。软件倒塌的地方是识别不知名的多部分姓氏,即使用逗号分隔。如果它能够破译这一点,那么所有数据的总错误数将少于1%。

Name Parts | Correct | Percent of Names in DB
 2             100%       48%
 3              98%       42%
 4              70%        9%
 5              45%        0.25%

答案 13 :(得分:1)

你可以做一些显而易见的事情:寻找Jr.,II,III等作为后缀,先生,夫人,博士等作为前缀并删除它们,然后第一个单词是名字,最后一个单词是姓氏,其间的一切都是中间名。除此之外,没有万无一失的解决方案。

一个完美的例子是David Lee Roth(姓:Roth)和Eddie Van Halen(姓:Van Halen)。如果Ann Marie Smith的第一个名字是“Ann Marie”,那么就无法将Ann与中间名称为Marie区分开来。

答案 14 :(得分:0)

我同意,对此没有简单的解决方案。但是我在VB 5.0的微软知识库文章中发现了一个糟糕的方法,这是在这里讨论的大部分讨论的实际实现:http://support.microsoft.com/kb/168799

这样的东西可以用在紧要关头。

答案 15 :(得分:0)

没有100%的方法可以做到这一点。

你可以拆分空格,并尝试理解你想要的名字,但是当它归结为它时,你有时会弄错。如果这足够好,请在这里找到任何可以分割的方法。

但是有些人的名字会像“John Wayne Olson”,其中“John Wayne”是第一个名字,而其他人的名字就像“John Wayne Olson”,其中“Wayne”是他们的中间名。该名称中没有任何内容可以告诉您解释它的方式。

就是这样。这是一个模拟世界。

我的规则很简单。

取最后一部分 - &gt;姓氏
如果剩下多个部分,请选择最后一部分 - &gt;中间名
还剩下什么 - &gt;名字

但不要认为这将是100%准确,也不会有任何其他硬编码解决方案。您需要能够让用户编辑他/她自己。

答案 16 :(得分:0)

这是一个愚蠢的差事。太多的例外能够确定性地做到这一点。如果你这样做是为了预先处理一份清单以供进一步审查,我会认为不那么肯定会更多。

  1. 删除称呼,标题和世代后缀(大正则表达式,或几个小名词)
  2. 如果只有一个名字,那就是'last'。
  3. 如果只有两个名字首先将它们分开,最后一次。
  4. 如果三个代币和中间首先将它们分开,中间,最后
  5. 手工排序。
  6. 任何进一步的处理几乎可以保证创造更多的工作,因为你必须重新组合你的处理拆分。

答案 17 :(得分:0)

我做了类似的事情。我遇到的主要问题是人们进入像“Richard R. Smith,Jr。”这样的东西。我在http://www.blackbeltcoder.com/Articles/strings/splitting-a-name-into-first-and-last-names发布了我的代码。它在C#中,但很容易转换为VB。

答案 18 :(得分:0)

你不想这样做,除非你只是联系一个文化的人。

例如:

Guido van Rossum的姓氏是van Rossum。

MIYAZAKI Hayao的名字是Hayao。

你能做的最大成功是剥夺常见的头衔和称呼,并尝试一些启发式方法。

即便如此,最简单的解决方案是只存储全名,或单独询问给定姓和姓氏。

答案 19 :(得分:0)

当然,有一个简单的解决方案 - 用空格分割字符串,计算令牌数量,如果有2,则将它们解释为FIRST和LAST名称,如果有3,则将其解释为FIRST,MIDDLE,最后一次。

问题在于简单的解决方案不是100%正确的解决方案 - 有人总是可以输入一个包含更多令牌的名称,或者可能包含标题,带有空格的姓氏(这可能吗?)等等你可以提出一个大多数时候都适用于大多数名字的解决方案,但不是绝对的解决方案。

我会按照Shad的建议拆分输入字段。

答案 20 :(得分:0)

我会说剥离salutations from a list然后按空格分割,将list.first()作为名字,将list.last()作为姓氏,然后用空格加入余数并将其作为中间名。并且全部显示结果并让用户修改它们!

答案 21 :(得分:0)

一种更简单的方法:

安装HumanNameParser NuGet:

Install-Package HumanNameParser

并调用解析扩展方法。

string name = "Mr Ali R Von Bayat III";

var result = name.Parse();

//result = new Name()
//{
//    Salutation = "Mr",
//    FirstName = "Ali",
//    MiddleInitials = "R",
//    LastName = "Von Bayat",
//    Suffix = "III"
//};

答案 22 :(得分:-1)

我同意不要这样做。 Rick Van DenBoer这个名字的最终名称是Van,但它是姓氏的一部分。