【问题标题】:Does anyone have a good Proper Case algorithm有没有人有一个好的正确案例算法
【发布时间】:2008-08-28 12:55:58
【问题描述】:

是否有人拥有受信任的 Proper Case 或 PCase 算法(类似于 UCase 或 Upper)?我正在寻找带有 "GEORGE BURDELL""george burdell" 之类的值并将其转换为 "George Burdell" 的东西。

我有一个处理简单案例的简单案例。理想的情况是拥有可以处理 "O'REILLY" 之类的东西并将其转换为 "O'Reilly" 的东西,但我知道这更难。

如果这样可以简化事情,我主要关注英语。


更新:我使用 C# 作为语言,但我几乎可以转换任何东西(假设存在类似的功能)。

我同意麦当劳的场景是一个艰难的场景。我的意思是在我的 O'Reilly 示例中提到这一点,但在原始帖子中没有。

【问题讨论】:

    标签: algorithm string


    【解决方案1】:

    除非我误解了你的问题,否则我认为你不需要自己动手,TextInfo 类可以为你做。

    using System.Globalization;
    
    CultureInfo.InvariantCulture.TextInfo.ToTitleCase("GeOrGE bUrdEll")
    

    将返回“George Burdell。如果涉及一些特殊规则,您可以使用自己的文化。

    更新:Michael(在对此答案的评论中)指出,如果输入全部大写,这将不起作用,因为该方法将假定它是首字母缩略词。天真的解决方法是在将文本提交到 ToTitleCase 之前对文本进行 .ToLower()。

    【讨论】:

    • 其实这是不正确的。您的示例将从文档中返回“GEORGE BURDELL”:通常,标题大小写将单词的第一个字符转换为大写,其余字符转换为小写。但是,完全大写的单词(例如首字母缩写词)不会被转换。
    • @Michael:对,你是......我想避免这种情况的简单方法是确保输入以小写开头。我将更新我的答案以反映这一点。
    • InvariantCulture 用于需要文化成分但与任何实际人类文化不匹配的操作。由于原始海报关注的是一种实际的人类语言(英语),因此有必要使用设置为英语的文化对象。
    【解决方案2】:

    @zwol:我会单独回复。

    这是一个基于ljspost 的示例。

    void Main()
    {
        List<string> names = new List<string>() {
            "bill o'reilly", 
            "johannes diderik van der waals", 
            "mr. moseley-williams", 
            "Joe VanWyck", 
            "mcdonald's", 
            "william the third", 
            "hrh prince charles", 
            "h.r.m. queen elizabeth the third",
            "william gates, iii", 
            "pope leo xii",
            "a.k. jennings"
        };
        
        names.Select(name => name.ToProperCase()).Dump();
    }
    
    // http://stackoverflow.com/questions/32149/does-anyone-have-a-good-proper-case-algorithm
    public static class ProperCaseHelper
    {
        public static string ToProperCase(this string input)
        {
            if (IsAllUpperOrAllLower(input))
            {
                // fix the ALL UPPERCASE or all lowercase names
                return string.Join(" ", input.Split(' ').Select(word => wordToProperCase(word)));
            }
            else
            {
                // leave the CamelCase or Propercase names alone
                return input;
            }
        }
    
        public static bool IsAllUpperOrAllLower(this string input)
        {
            return (input.ToLower().Equals(input) || input.ToUpper().Equals(input));
        }
    
        private static string wordToProperCase(string word)
        {
            if (string.IsNullOrEmpty(word)) return word;
    
            // Standard case
            string ret = capitaliseFirstLetter(word);
    
            // Special cases:
            ret = properSuffix(ret, "'");   // D'Artagnon, D'Silva
            ret = properSuffix(ret, ".");   // ???
            ret = properSuffix(ret, "-");       // Oscar-Meyer-Weiner
            ret = properSuffix(ret, "Mc", t => t.Length > 4);      // Scots
            ret = properSuffix(ret, "Mac", t => t.Length > 5);     // Scots except Macey
    
            // Special words:
            ret = specialWords(ret, "van");     // Dick van Dyke
            ret = specialWords(ret, "von");     // Baron von Bruin-Valt
            ret = specialWords(ret, "de");
            ret = specialWords(ret, "di");
            ret = specialWords(ret, "da");      // Leonardo da Vinci, Eduardo da Silva
            ret = specialWords(ret, "of");      // The Grand Old Duke of York
            ret = specialWords(ret, "the");     // William the Conqueror
            ret = specialWords(ret, "HRH");     // His/Her Royal Highness
            ret = specialWords(ret, "HRM");     // His/Her Royal Majesty
            ret = specialWords(ret, "H.R.H.");  // His/Her Royal Highness
            ret = specialWords(ret, "H.R.M.");  // His/Her Royal Majesty
    
            ret = dealWithRomanNumerals(ret);   // William Gates, III
    
            return ret;
        }
    
        private static string properSuffix(string word, string prefix, Func<string, bool> condition = null)
        {
            if (string.IsNullOrEmpty(word)) return word;
            if (condition != null && ! condition(word)) return word;
            
            string lowerWord = word.ToLower();
            string lowerPrefix = prefix.ToLower();
    
            if (!lowerWord.Contains(lowerPrefix)) return word;
    
            int index = lowerWord.IndexOf(lowerPrefix);
    
            // If the search string is at the end of the word ignore.
            if (index + prefix.Length == word.Length) return word;
    
            return word.Substring(0, index) + prefix +
                capitaliseFirstLetter(word.Substring(index + prefix.Length));
        }
    
        private static string specialWords(string word, string specialWord)
        {
            if (word.Equals(specialWord, StringComparison.InvariantCultureIgnoreCase))
            {
                return specialWord;
            }
            else
            {
                return word;
            }
        }
    
        private static string dealWithRomanNumerals(string word)
        {
            // Roman Numeral parser thanks to [djk](https://stackoverflow.com/users/785111/djk)
            // Note that it excludes the Chinese last name Xi
            return new Regex(@"\b(?!Xi\b)(X|XX|XXX|XL|L|LX|LXX|LXXX|XC|C)?(I|II|III|IV|V|VI|VII|VIII|IX)?\b", RegexOptions.IgnoreCase).Replace(word, match => match.Value.ToUpperInvariant());
        }
    
        private static string capitaliseFirstLetter(string word)
        {
            return char.ToUpper(word[0]) + word.Substring(1).ToLower();
        }
    
    }
    

    【讨论】:

    • 我们把它放到我们的生产系统中。我们的一位客户花了整整 15 分钟来询问为什么将“Macey”设置为“MacEy”......所以我们删除了那行特定的代码并保留了其他所有内容。谢谢!
    • 谢谢!其实我也认识一个梅西。嗯...当我有时间时,我会在 Wikipedia 页面上搜索所有 MiXeD 大小写单词的苏格兰盖尔语名称,并将其添加进去。en.wikipedia.org/wiki/List_of_Scottish_Gaelic_surnames
    • 如果罗马数字紧挨着一个符号,例如“Sir William III.”,这将失败。我把 DealWithRomanNumerals 改成了这个单行,效果很好:return new Regex(@"\b(?!Xi\b)(X|XX|XXX|XL|L|LX|LXX|LXXX|XC|C)?(I|II|III|IV|V|VI|VII|VIII|IX)?\b", RegexOptions.IgnoreCase).Replace(word, match =&gt; match.Value.ToUpperInvariant()); -- 也过滤掉了常用的中文名字“Xi”。
    【解决方案3】:

    还有这个用于标题大小写文本的简洁 Perl 脚本。

    http://daringfireball.net/2008/08/title_case_update

    #!/usr/bin/perl
    
    #     This filter changes all words to Title Caps, and attempts to be clever
    # about *un*capitalizing small words like a/an/the in the input.
    #
    # The list of "small words" which are not capped comes from
    # the New York Times Manual of Style, plus 'vs' and 'v'. 
    #
    # 10 May 2008
    # Original version by John Gruber:
    # http://daringfireball.net/2008/05/title_case
    #
    # 28 July 2008
    # Re-written and much improved by Aristotle Pagaltzis:
    # http://plasmasturm.org/code/titlecase/
    #
    #   Full change log at __END__.
    #
    # License: http://www.opensource.org/licenses/mit-license.php
    #
    
    
    use strict;
    use warnings;
    use utf8;
    use open qw( :encoding(UTF-8) :std );
    
    
    my @small_words = qw( (?<!q&)a an and as at(?!&t) but by en for if in of on or the to v[.]? via vs[.]? );
    my $small_re = join '|', @small_words;
    
    my $apos = qr/ (?: ['’] [[:lower:]]* )? /x;
    
    while ( <> ) {
      s{\A\s+}{}, s{\s+\z}{};
    
      $_ = lc $_ if not /[[:lower:]]/;
    
      s{
          \b (_*) (?:
              ( (?<=[ ][/\\]) [[:alpha:]]+ [-_[:alpha:]/\\]+ |   # file path or
                [-_[:alpha:]]+ [@.:] [-_[:alpha:]@.:/]+ $apos )  # URL, domain, or email
              |
              ( (?i: $small_re ) $apos )                         # or small word (case-insensitive)
              |
              ( [[:alpha:]] [[:lower:]'’()\[\]{}]* $apos )       # or word w/o internal caps
              |
              ( [[:alpha:]] [[:alpha:]'’()\[\]{}]* $apos )       # or some other word
          ) (_*) \b
      }{
          $1 . (
            defined $2 ? $2         # preserve URL, domain, or email
          : defined $3 ? "\L$3"     # lowercase small word
          : defined $4 ? "\u\L$4"   # capitalize word w/o internal caps
          : $5                      # preserve other kinds of word
          ) . $6
      }xeg;
    
    
      # Exceptions for small words: capitalize at start and end of title
      s{
          (  \A [[:punct:]]*         # start of title...
          |  [:.;?!][ ]+             # or of subsentence...
          |  [ ]['"“‘(\[][ ]*     )  # or of inserted subphrase...
          ( $small_re ) \b           # ... followed by small word
      }{$1\u\L$2}xig;
    
      s{
          \b ( $small_re )      # small word...
          (?= [[:punct:]]* \Z   # ... at the end of the title...
          |   ['"’”)\]] [ ] )   # ... or of an inserted subphrase?
      }{\u\L$1}xig;
    
      # Exceptions for small words in hyphenated compound words
      ## e.g. "in-flight" -> In-Flight
      s{
          \b
          (?<! -)                 # Negative lookbehind for a hyphen; we don't want to match man-in-the-middle but do want (in-flight)
          ( $small_re )
          (?= -[[:alpha:]]+)      # lookahead for "-someword"
      }{\u\L$1}xig;
    
      ## # e.g. "Stand-in" -> "Stand-In" (Stand is already capped at this point)
      s{
          \b
          (?<!…)                  # Negative lookbehind for a hyphen; we don't want to match man-in-the-middle but do want (stand-in)
          ( [[:alpha:]]+- )       # $1 = first word and hyphen, should already be properly capped
          ( $small_re )           # ... followed by small word
          (?! - )                 # Negative lookahead for another '-'
      }{$1\u$2}xig;
    
      print "$_";
    }
    
    __END__
    

    但听起来你的意思是正确的情况......对于人的名字仅限

    【讨论】:

      【解决方案4】:

      我做了一个基于 Lingua::EN::NameCase 的快速 C# 端口 https://github.com/tamtamchik/namecase

      public static class CIQNameCase
      {
          static Dictionary<string, string> _exceptions = new Dictionary<string, string>
              {
                  {@"\bMacEdo"     ,"Macedo"},
                  {@"\bMacEvicius" ,"Macevicius"},
                  {@"\bMacHado"    ,"Machado"},
                  {@"\bMacHar"     ,"Machar"},
                  {@"\bMacHin"     ,"Machin"},
                  {@"\bMacHlin"    ,"Machlin"},
                  {@"\bMacIas"     ,"Macias"},
                  {@"\bMacIulis"   ,"Maciulis"},
                  {@"\bMacKie"     ,"Mackie"},
                  {@"\bMacKle"     ,"Mackle"},
                  {@"\bMacKlin"    ,"Macklin"},
                  {@"\bMacKmin"    ,"Mackmin"},
                  {@"\bMacQuarie"  ,"Macquarie"}
              };
      
          static Dictionary<string, string> _replacements = new Dictionary<string, string>
              {
                  {@"\bAl(?=\s+\w)"         , @"al"},        // al Arabic or forename Al.
                  {@"\b(Bin|Binti|Binte)\b" , @"bin"},       // bin, binti, binte Arabic
                  {@"\bAp\b"                , @"ap"},        // ap Welsh.
                  {@"\bBen(?=\s+\w)"        , @"ben"},       // ben Hebrew or forename Ben.
                  {@"\bDell([ae])\b"        , @"dell$1"},    // della and delle Italian.
                  {@"\bD([aeiou])\b"        , @"d$1"},       // da, de, di Italian; du French; do Brasil
                  {@"\bD([ao]s)\b"          , @"d$1"},       // das, dos Brasileiros
                  {@"\bDe([lrn])\b"         , @"de$1"},      // del Italian; der/den Dutch/Flemish.
                  {@"\bEl\b"                , @"el"},        // el Greek or El Spanish.
                  {@"\bLa\b"                , @"la"},        // la French or La Spanish.
                  {@"\bL([eo])\b"           , @"l$1"},       // lo Italian; le French.
                  {@"\bVan(?=\s+\w)"        , @"van"},       // van German or forename Van.
                  {@"\bVon\b"               , @"von"}        // von Dutch/Flemish
              };
      
          static string[] _conjunctions = { "Y", "E", "I" };
      
          static string _romanRegex = @"\b((?:[Xx]{1,3}|[Xx][Ll]|[Ll][Xx]{0,3})?(?:[Ii]{1,3}|[Ii][VvXx]|[Vv][Ii]{0,3})?)\b";
      
          /// <summary>
          /// Case a name field into its appropriate case format 
          /// e.g. Smith, de la Cruz, Mary-Jane,  O'Brien, McTaggart
          /// </summary>
          /// <param name="nameString"></param>
          /// <returns></returns>
          public static string NameCase(string nameString)
          {
              // Capitalize
              nameString = Capitalize(nameString);
              nameString = UpdateIrish(nameString);
      
              // Fixes for "son (daughter) of" etc
              foreach (var replacement in _replacements.Keys)
              {
                  if (Regex.IsMatch(nameString, replacement))
                  {
                      Regex rgx = new Regex(replacement);
                      nameString = rgx.Replace(nameString, _replacements[replacement]);
                  }                    
              }
      
              nameString = UpdateRoman(nameString);
              nameString = FixConjunction(nameString);
      
              return nameString;
          }
      
          /// <summary>
          /// Capitalize first letters.
          /// </summary>
          /// <param name="nameString"></param>
          /// <returns></returns>
          private static string Capitalize(string nameString)
          {
              nameString = nameString.ToLower();
              nameString = Regex.Replace(nameString, @"\b\w", x => x.ToString().ToUpper());
              nameString = Regex.Replace(nameString, @"'\w\b", x => x.ToString().ToLower()); // Lowercase 's
              return nameString;
          }
      
          /// <summary>
          /// Update for Irish names.
          /// </summary>
          /// <param name="nameString"></param>
          /// <returns></returns>
          private static string UpdateIrish(string nameString)
          {
              if(Regex.IsMatch(nameString, @".*?\bMac[A-Za-z^aciozj]{2,}\b") || Regex.IsMatch(nameString, @".*?\bMc"))
              {
                  nameString = UpdateMac(nameString);
              }            
              return nameString;
          }
      
          /// <summary>
          /// Updates irish Mac & Mc.
          /// </summary>
          /// <param name="nameString"></param>
          /// <returns></returns>
          private static string UpdateMac(string nameString)
          {
              MatchCollection matches = Regex.Matches(nameString, @"\b(Ma?c)([A-Za-z]+)");
              if(matches.Count == 1 && matches[0].Groups.Count == 3)
              {
                  string replacement = matches[0].Groups[1].Value;
                  replacement += matches[0].Groups[2].Value.Substring(0, 1).ToUpper();
                  replacement += matches[0].Groups[2].Value.Substring(1);
                  nameString = nameString.Replace(matches[0].Groups[0].Value, replacement);
      
                  // Now fix "Mac" exceptions
                  foreach (var exception in _exceptions.Keys)
                  {
                      nameString = Regex.Replace(nameString, exception, _exceptions[exception]);
                  }
              }
              return nameString;
          }
      
          /// <summary>
          /// Fix roman numeral names.
          /// </summary>
          /// <param name="nameString"></param>
          /// <returns></returns>
          private static string UpdateRoman(string nameString)
          {
              MatchCollection matches = Regex.Matches(nameString, _romanRegex);
              if (matches.Count > 1)
              {
                  foreach(Match match in matches)
                  {
                      if(!string.IsNullOrEmpty(match.Value))
                      {
                          nameString = Regex.Replace(nameString, match.Value, x => x.ToString().ToUpper());
                      }
                  }
              }
              return nameString;
          }
      
          /// <summary>
          /// Fix Spanish conjunctions.
          /// </summary>
          /// <param name=""></param>
          /// <returns></returns>
          private static string FixConjunction(string nameString)
          {            
              foreach (var conjunction in _conjunctions)
              {
                  nameString = Regex.Replace(nameString, @"\b" + conjunction + @"\b", x => x.ToString().ToLower());
              }
              return nameString;
          }
      }
      

      用法

      string name_cased = CIQNameCase.NameCase("McCarthy");
      

      这是我的测试方法,一切似乎都通过了:

      [TestMethod]
      public void Test_NameCase_1()
      {
          string[] names = {
              "Keith", "Yuri's", "Leigh-Williams", "McCarthy",
              // Mac exceptions
              "Machin", "Machlin", "Machar",
              "Mackle", "Macklin", "Mackie",
              "Macquarie", "Machado", "Macevicius",
              "Maciulis", "Macias", "MacMurdo",
              // General
              "O'Callaghan", "St. John", "von Streit",
              "van Dyke", "Van", "ap Llwyd Dafydd",
              "al Fahd", "Al",
              "el Grecco",
              "ben Gurion", "Ben",
              "da Vinci",
              "di Caprio", "du Pont", "de Legate",
              "del Crond", "der Sind", "van der Post", "van den Thillart",
              "von Trapp", "la Poisson", "le Figaro",
              "Mack Knife", "Dougal MacDonald",
              "Ruiz y Picasso", "Dato e Iradier", "Mas i Gavarró",
              // Roman numerals
              "Henry VIII", "Louis III", "Louis XIV",
              "Charles II", "Fred XLIX", "Yusof bin Ishak",
          };
      
          foreach(string name in names)
          {
              string name_upper = name.ToUpper();
              string name_cased = CIQNameCase.NameCase(name_upper);
              Console.WriteLine(string.Format("name: {0} -> {1}  -> {2}", name, name_upper, name_cased));
              Assert.IsTrue(name == name_cased);
          }
      
      }
      

      【讨论】:

      【解决方案5】:

      我今天写这个是为了在我正在开发的应用程序中实现。我认为这段代码对于 cmets 来说是非常不言自明的。它并非在所有情况下都 100% 准确,但它可以轻松处理您的大部分西方名字。

      例子:

      mary-jane =&gt; Mary-Jane

      o'brien =&gt; O'Brien

      Joël VON WINTEREGG =&gt; Joël von Winteregg

      jose de la acosta =&gt; Jose de la Acosta

      代码是可扩展的,您可以将任何字符串值添加到顶部的数组中以满足您的需要。请研究它并添加任何可能需要的特殊功能。

      function name_title_case($str)
      {
        // name parts that should be lowercase in most cases
        $ok_to_be_lower = array('av','af','da','dal','de','del','der','di','la','le','van','der','den','vel','von');
        // name parts that should be lower even if at the beginning of a name
        $always_lower   = array('van', 'der');
      
        // Create an array from the parts of the string passed in
        $parts = explode(" ", mb_strtolower($str));
      
        foreach ($parts as $part)
        {
          (in_array($part, $ok_to_be_lower)) ? $rules[$part] = 'nocaps' : $rules[$part] = 'caps';
        }
      
        // Determine the first part in the string
        reset($rules);
        $first_part = key($rules);
      
        // Loop through and cap-or-dont-cap
        foreach ($rules as $part => $rule)
        {
          if ($rule == 'caps')
          {
            // ucfirst() words and also takes into account apostrophes and hyphens like this:
            // O'brien -> O'Brien || mary-kaye -> Mary-Kaye
            $part = str_replace('- ','-',ucwords(str_replace('-','- ', $part)));
            $c13n[] = str_replace('\' ', '\'', ucwords(str_replace('\'', '\' ', $part)));
          }
          else if ($part == $first_part && !in_array($part, $always_lower))
          {
            // If the first part of the string is ok_to_be_lower, cap it anyway
            $c13n[] = ucfirst($part);
          }
          else
          {
            $c13n[] = $part;
          }
        }
      
        $titleized = implode(' ', $c13n);
      
        return trim($titleized);
      }
      

      【讨论】:

        【解决方案6】:

        您使用什么编程语言?许多语言允许正则表达式匹配的回调函数。这些可以用来轻松地正确匹配匹配。使用的正则表达式非常简单,只需匹配所有单词字符,如下所示:

        /\w+/
        

        或者,您可以提取第一个字符作为额外匹配:

        /(\w)(\w*)/
        

        现在您可以分别访问匹配中的第一个字符和后续字符。然后,回调函数可以简单地返回命中的串联。在伪 Python 中(我实际上并不了解 Python):

        def make_proper(match):
            return match[1].to_upper + match[2]
        

        顺便说一句,这也可以处理“O'Reilly”的情况,因为“O”和“Reilly”将分别匹配并且都正确大小写。然而,还有其他特殊情况没有被算法很好地处理,例如“McDonald's”或通常是任何撇号的词。该算法将为后者生成“麦当劳”。可以对撇号进行特殊处理,但这会干扰第一种情况。不可能找到一个完美的解决方案。在实践中,考虑撇号后部分的长度可能会有所帮助。

        【讨论】:

          【解决方案7】:

          这可能是一个幼稚的 C# 实现:-

          public class ProperCaseHelper {
            public string ToProperCase(string input) {
              string ret = string.Empty;
          
              var words = input.Split(' ');
          
              for (int i = 0; i < words.Length; ++i) {
                ret += wordToProperCase(words[i]);
                if (i < words.Length - 1) ret += " ";
              }
          
              return ret;
            }
          
            private string wordToProperCase(string word) {
              if (string.IsNullOrEmpty(word)) return word;
          
              // Standard case
              string ret = capitaliseFirstLetter(word);
          
              // Special cases:
              ret = properSuffix(ret, "'");
              ret = properSuffix(ret, ".");
              ret = properSuffix(ret, "Mc");
              ret = properSuffix(ret, "Mac");
          
              return ret;
            }
          
            private string properSuffix(string word, string prefix) {
              if(string.IsNullOrEmpty(word)) return word;
          
              string lowerWord = word.ToLower(), lowerPrefix = prefix.ToLower();
              if (!lowerWord.Contains(lowerPrefix)) return word;
          
              int index = lowerWord.IndexOf(lowerPrefix);
          
              // If the search string is at the end of the word ignore.
              if (index + prefix.Length == word.Length) return word;
          
              return word.Substring(0, index) + prefix +
                capitaliseFirstLetter(word.Substring(index + prefix.Length));
            }
          
            private string capitaliseFirstLetter(string word) {
              return char.ToUpper(word[0]) + word.Substring(1).ToLower();
            }
          }
          

          【讨论】:

          • @Colin:发布您的版本作为自己的答案,不要如此激进地编辑别人的答案。
          【解决方案8】:

          每个单词首字母大写的简单方法(用空格隔开)

          $words = explode(” “, $string);
          for ($i=0; $i<count($words); $i++) {
          $s = strtolower($words[$i]);
          $s = substr_replace($s, strtoupper(substr($s, 0, 1)), 0, 1);
          $result .= “$s “;
          }
          $string = trim($result);
          

          就捕捉您给出的“O'REILLY”示例而言 在两个空格和 ' 上拆分字符串将不起作用,因为它将大写出现在撇号之后的任何字母,即 Fred 中的 s

          所以我可能会尝试类似的东西

          $words = explode(” “, $string);
          for ($i=0; $i<count($words); $i++) {
          
          $s = strtolower($words[$i]);
          
          if (substr($s, 0, 2) === "o'"){
          $s = substr_replace($s, strtoupper(substr($s, 0, 3)), 0, 3);
          }else{
          $s = substr_replace($s, strtoupper(substr($s, 0, 1)), 0, 1);
          }
          $result .= “$s “;
          }
          $string = trim($result);
          

          这应该会抓住 O'Reilly、O'Clock、O'Donnell 等,希望对您有所帮助

          请注意此代码未经测试。

          【讨论】:

            【解决方案9】:

            克罗诺兹,谢谢。我在你的函数中发现了这一行:

            `if (!lowerWord.Contains(lowerPrefix)) return word`;
            

            必须说

            if (!lowerWord.StartsWith(lowerPrefix)) return word;
            

            所以“información”不会更改为“InforMacIón”

            最好的,

            恩里克

            【讨论】:

              【解决方案10】:

              我使用它作为文本框的 textchanged 事件处理程序。支持“麦当劳”入驻

              Public Shared Function DoProperCaseConvert(ByVal str As String, Optional ByVal allowCapital As Boolean = True) As String
                  Dim strCon As String = ""
                  Dim wordbreak As String = " ,.1234567890;/\-()#$%^&*€!~+=@"
                  Dim nextShouldBeCapital As Boolean = True
              
                  'Improve to recognize all caps input
                  'If str.Equals(str.ToUpper) Then
                  '    str = str.ToLower
                  'End If
              
                  For Each s As Char In str.ToCharArray
              
                      If allowCapital Then
                          strCon = strCon & If(nextShouldBeCapital, s.ToString.ToUpper, s)
                      Else
                          strCon = strCon & If(nextShouldBeCapital, s.ToString.ToUpper, s.ToLower)
                      End If
              
                      If wordbreak.Contains(s.ToString) Then
                          nextShouldBeCapital = True
                      Else
                          nextShouldBeCapital = False
                      End If
                  Next
              
                  Return strCon
              End Function
              

              【讨论】:

              • 分词是否有理由包括墨西哥比索、美元和爱尔兰欧元,但不包括英镑?分词是否有理由不包括下划线?
              • 只是没有。您可以将这些字符中的任何一个放在数组中。虽然如果是讽刺的话,我认为它不能放在那里。
              【解决方案11】:

              这里有很多很好的答案。我的很简单,只考虑我们在组织中的名称。您可以根据需要扩展它。这不是一个完美的解决方案,并且会将 vancouver 更改为 VanCouver,这是错误的。因此,如果您使用它,请对其进行调整。

              这是我在 C# 中的解决方案。这会将名称硬编码到程序中,但通过一些工作,您可以在程序外部保留一个文本文件并读取名称异常(即 Van、Mc、Mac)并循环遍历它们。

              public static String toProperName(String name)
              {
                  if (name != null)
                  {
                      if (name.Length >= 2 && name.ToLower().Substring(0, 2) == "mc")  // Changes mcdonald to "McDonald"
                          return "Mc" + Regex.Replace(name.ToLower().Substring(2), @"\b[a-z]", m => m.Value.ToUpper());
              
                      if (name.Length >= 3 && name.ToLower().Substring(0, 3) == "van")  // Changes vanwinkle to "VanWinkle"
                          return "Van" + Regex.Replace(name.ToLower().Substring(3), @"\b[a-z]", m => m.Value.ToUpper());
              
                      return Regex.Replace(name.ToLower(), @"\b[a-z]", m => m.Value.ToUpper());  // Changes to title case but also fixes 
                                                                                                 // appostrophes like O'HARE or o'hare to O'Hare
                  }
              
                  return "";
              }
              

              【讨论】:

                【解决方案12】:

                我知道这个帖子已经打开了一段时间,但是当我研究这个问题时,我偶然发现了这个漂亮的网站,它可以让你快速粘贴名字以大写:https://dialect.ca/code/name-case/。我想将其包含在此处,以供其他从事类似研究/项目的人参考。

                他们在这个链接上发布了他们用 php 编写的算法:https://dialect.ca/code/name-case/name_case.phps

                初步测试和阅读他们的代码表明他们已经相当彻底。

                【讨论】:

                  【解决方案13】:

                  您没有提及您希望解决方案使用哪种语言,所以这里是一些伪代码。

                  Loop through each character
                      If the previous character was an alphabet letter
                          Make the character lower case
                      Otherwise
                          Make the character upper case
                  End loop
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-08-06
                    • 1970-01-01
                    • 2011-11-19
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多