代码之家  ›  专栏  ›  技术社区  ›  Kelsey

如何改进将名称转换为正确大小写的方法?

  •  17
  • Kelsey  · 技术社区  · 14 年前

    我正在编写一个基本函数,在一次批处理过程中,将数百万个名称从当前的大写形式转换为正确的混合大小写。我想到了以下功能:

    public string ConvertToProperNameCase(string input)
    {
        char[] chars = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input.ToLower()).ToCharArray();
    
        for (int i = 0; i + 1 < chars.Length; i++)
        {
            if ((chars[i].Equals('\'')) ||
                (chars[i].Equals('-')))
            {                    
                chars[i + 1] = Char.ToUpper(chars[i + 1]);
            }
        }
        return new string(chars);
    }
    

    它在大多数情况下工作,例如:

    1. 约翰·史密斯→约翰·史密斯
    2. 史密斯,约翰T→史密斯,约翰T
    3. 约翰·奥布莱恩→约翰·奥布莱恩
    4. 约翰多史密斯→约翰多史密斯

    有些边缘情况不起作用:

    1. Jason McDonald→Jason McDonald(正确:Jason McDonald)
    2. Oscar de la Hoya→Oscar de la Hoya(正确:Oscar de la Hoya)
    3. 玛丽·迪弗兰科→玛丽·迪弗兰科(正确:玛丽·迪弗兰科)

    这些都没有抓到,我不确定我是否能处理所有这些奇怪的边缘案件。如何更改或添加以捕获更多边缘案例?我肯定还有很多我都没想到的边缘案例。所有的大小写也应该遵循北美的惯例,这意味着如果某些国家期望不同的大写格式,那么北美格式优先。

    9 回复  |  直到 13 年前
        1
  •  9
  •   Johannes Rudolph    14 年前

    我认为你会在这里再次碰壁,因为通常你无法正确判断转换是否合理。

    考虑一下你的优势案例

    杰森•麦克唐纳->杰森•麦克唐纳 (正确:杰森·麦克唐纳)

    你可以简单地在你名字的开头查一下mc,然后申请你的更正,对吧?但是,如果你的人叫麦克伊兹克(当然是我编造的),那不应该更正为麦克伊兹克,而应该保持原样呢?

    这个问题没有100%完美的解决方案。这里有一个自然语言问题,很难解决,特别是对于计算机来说。文化差异太大,无法正确建模。即使你说北美公约优先,你也会有很高比例的“误报”。我们的社会是由多种文化组成的,仅仅说“北美优先”是不够的。

    如果不处理边缘案例,我想你目前的解决方案99%的时间都会奏效。如果确实需要100%正确的名称,则应手动更正所有其他边缘情况。

        2
  •  5
  •   John Fisher    14 年前

    我希望您进行此转换的原因是,软件正在进行更改,以允许用户首先输入正确大小写的姓名。

    也就是说,唯一可靠的解决方案是通知用户您已经更改了他们的名称表示形式。如果不正确,他们可以编辑外壳。(你可以给他们打电话,发邮件,等到他们下次使用你的软件,等等)

    如果你不能让用户更新他们自己的名字,第二个最可靠的方法是从公共来源收集(姓氏)名单。如果你能找到足够的这些,你应该能够涵盖更多的边缘情况-简单地看看名称是否存在于你的适当大小写列表中,然后使用该大小写。

        3
  •  3
  •   Cory Petosky    14 年前

    这个问题没有一般的解决办法。即使在像“mc”这样的常见边缘案例中,也有反例。我在大学里有一个朋友有一个“mc”的名字,他没有把下面的字符大写;很明显,这是移民几代人以前搞砸的,他们都坚持记录在案但历史上拼写错误。

    我同事的名字之一是两个传统的名字加在一起。你永远也解释不了。

    这个问题相当于提升视频文件的比例;您可以尽可能地接近最佳状态,但无法神奇地生成最初未存储的信息。

        4
  •  1
  •   Nelson Rothermel    14 年前

    你可以制定一些规则来拉近距离,但是你不能百分之百地做到。例如,可以创建前缀列表(mc、di等)

    1. 如果前缀以元音结尾,下一个字母是元音,则小写。
    2. 如果前缀以元音结尾,下一个字母是辅音,大写。
    3. 如果前缀以辅音结尾,则下一个字母为大写。

    等。。。但您可能希望获得一个良好的前缀列表,并且您将始终有例外。

        5
  •  0
  •   Mark    14 年前

    你可以

    • 分隔符“,”和“-”
    • 标题案例每个部分
    • 处理每个短语的所有边缘情况
        6
  •  0
  •   Jacob G    14 年前

    问题是,正如其他人所说,你永远不会抓住每一个边缘案例。我打算建议你去 here ,下载完整的数据集并进行比较。但是,这个数据集都是大写的。因为这是一个一次性的过程,所以我会从前面提到的1000个姓氏的链接下载列表,手动更正它们,并根据该列表处理您的记录。标记那些未处理的记录,看看这个数字是否小到可以手工管理。

        7
  •  0
  •   hackerhasid    14 年前

    首先,如果名称的结尾有“或-,这段代码将抛出一个异常,因为它将尝试将数组中的下一个(不存在的)元素大写。 编辑,请参阅下面的注释

    除此之外…

    我不认为你能真正解释迪佛朗哥,除非你 只有 为迪弗兰科而不是其他 有吗?另外,我认为可以肯定的是,任何一个mc下一个字母都应该大写。我也认为,可以肯定地说,德和拉的周围空间可以较低的情况下。

    但到了最后,你似乎在试图利用文化,这对我来说,也许你不仅仅是在使用英语。如果是这样的话,我想你会有比你想象中更多的问题。如果你只学英语(或者这个模块是英语模块,还有其他语言的模块),那么也许你已经尽可能接近了(除了mc等)。

        8
  •  0
  •   Grace Note    14 年前

    你的问题是你的计划是否可以改进。我的回答是,“改进的方向是什么?”您有两个相互排斥的不同边缘案例。要么你抓不到有不寻常资本化规则的人,要么你抓不到不遵守不寻常资本化规则的人。

    我和一个姓“德拉罗萨”的人一起上学。考虑到你的例子德拉霍亚,这将是公平的假设,“德拉罗萨”也是一个姓氏的人在那里。所以如果你用一种方法去斩首“德拉”,那么你会想念我的朋友,我会很难过。如果你不实行斩首,你就会错过其他人。上帝也不会让你碰到一个德拉罗萨,他不会被任何方法抓住…

    那么,您认为代码的“改进”方向是什么?如果你认为你应该处理不寻常的资本化的边缘案例,并手动解释那些不遵守,其他提供的答案将帮助你实现这一目标。如果您认为应该手动处理异常大小写,那么您的代码不需要更改。不管怎样,你都得手动操作。

        9
  •  0
  •   James    13 年前
    <System.Runtime.CompilerServices.Extension()> _
    Public Function ProperCase(ByVal value As String) As String
    
        If String.IsNullOrWhiteSpace(value) Then
            Return String.Empty
        End If
    
        value = value.Trim
    
        Dim sb As New StringBuilder(CultureInfo.CurrentCulture.TextInfo.ToTitleCase(value.ToLower))
    
        '// Special cases ' and -
        For i As Integer = 0 To sb.Length
            Dim c As Char = sb(i)
            If sb(i).Equals("'") Or sb(i).Equals("-") Then
                'Upper Case Next character
                sb(i + 1) = Char.ToUpper(sb(i + 1))
            End If
        Next
    
        If sb.ToString.StartsWith("Mac") Then
            sb(3) = Char.ToUpper(sb(3))
        End If
    
        If sb.ToString.StartsWith("Mc") Then
            sb(2) = Char.ToUpper(sb(2))
        End If
    
        Return sb.ToString
    
    End Function