2008-08-28 19 views
23

¿Alguien tiene un algoritmo de Caso Propio o PCase de confianza (similar a un UCase o Superior)? Estoy buscando algo que tenga un valor como "GEORGE BURDELL" o "george burdell" y lo convierta en "George Burdell".¿Alguien tiene un buen algoritmo de Caso Propio?

Tengo uno simple que maneja casos simples. Lo ideal sería tener algo que pueda manejar cosas como "O'REILLY" y convertirlo en "O'Reilly", pero sé que es más difícil.

Estoy principalmente centrado en el idioma inglés si eso simplifica las cosas.


ACTUALIZACIÓN: estoy usando C# como lenguaje, pero puedo convertir de casi cualquier cosa (asumiendo como la funcionalidad existe).

Estoy de acuerdo en que el McDonald's scneario es difícil. Quise mencionar eso junto con mi ejemplo de O'Reilly, pero no en la publicación original.

Respuesta

18

A menos que haya entendido mal su pregunta, no creo que tenga que hacer la suya propia, la clase TextInfo puede hacerlo por usted.

using System.Globalization; 

CultureInfo.InvariantCulture.TextInfo.ToTitleCase("GeOrGE bUrdEll") 

volveremos "George Burdell y usted puede utilizar su propia cultura si hay algunas reglas especiales que participan

Actualización:..Michael (en un comentario a esta respuesta) señaló que con ello no se funcionar si la entrada es todo mayúsculas ya que el método supondrá que es un acrónimo. La solución ingenua para esto es .ToLower() el texto antes de enviarlo a ToTitleCase.

+1

En realidad, esto es incorrecto. Su ejemplo devolverá "GEORGE BURDELL" De los documentos: Generalmente, la carcasa del título convierte el primer carácter de una palabra en mayúscula y el resto de los caracteres en minúscula. Sin embargo, una palabra que está completamente en mayúsculas, como un acrónimo, no se convierte. –

+0

@Michael: Correcto, usted ... Supongo que la forma más fácil de evitar eso sería asegurar que la entrada esté en un nivel inferior para comenzar. Actualizaré mi respuesta para reflejar esto. –

+0

InvariantCulture se utiliza para operaciones que requieren un componente cultural pero que no concuerdan con ninguna cultura humana real. Como el póster original está enfocado en un lenguaje humano real (inglés), es necesario usar un objeto cultural configurado en inglés. –

1

¿Qué lenguaje de programación usas? Muchos idiomas permiten funciones de devolución de llamada para las coincidencias de expresiones regulares. Estos se pueden usar para adaptar la coincidencia fácilmente. La expresión regular que sería utilizado es muy sencillo, sólo tiene que coincidir todos los caracteres de palabra, así:

/\w+/ 

Por otra parte, ya se puede extraer el primer carácter a ser un partido extra:

/(\w)(\w*)/ 

Ahora puedes acceder al primer personaje y a los personajes sucesivos de la partida por separado. La función de devolución de llamada puede simplemente devolver una concatenación de los hits. En seudo Python (que en realidad no sé Python):

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

Por cierto, esto también manejar el caso de “O'Reilly”, porque “O” y “Reilly” sería ser igualada por separado y ambos propercased. Sin embargo, hay otros casos especiales que el algoritmo no maneja bien, p. "McDonald's" o, en general, cualquier palabra apostroficada. El algoritmo produciría "Mcdonald'S" para este último. Se podría implementar un manejo especial para el apóstrofo, pero eso interferiría con el primer caso. Encontrar una solución perfecta no es posible. En la práctica, podría ser útil considerar la longitud de la parte después del apóstrofo.

0

una forma sencilla de mayúscula la primera letra de cada palabra (separadas por un espacio)

$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); 

en términos de controlar el ejemplo "O'REILLY" que dio división de la cadena en ambos espacios y ' no funcionaría, ya que sería mayúscula una carta que apareció después de un apostraphe es decir, los s en algo de Fred

así que probablemente probar como

$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); 

Esto debería atrapar a O'Reilly, O'Clock, O'Donnell, etc. Espero que ayude

Tenga en cuenta que este código no ha sido probado.

-1

No mencionas en qué idioma quieres que esté la solución, así que aquí tienes un pseudo código.

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 
4

También existe este limpio script de Perl para el texto de la cubierta del título.

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__ 

Pero parece que a caso adecuado quiere decir .. para los nombres de las personas única.

1

Aquí hay una implementación de C# quizás ingenua: -

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(); 
    } 
} 
+1

@Colin: publique su versión como su propia respuesta, no edite la respuesta de otra persona tan radicalmente. – zwol

0

Kronoz, gracias. He encontrado en su función que la línea:

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

debe decir

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

por lo que "información" no se cambia a "información"

mejor,

Enrique

0

Utilizo esto como el controlador de eventos text-change de cuadros de texto. Entrada de soporte de "McDonald"

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;/\-()#$%^&*€[email protected]" 
    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 
+0

¿Hay alguna razón para que los saltos de palabra incluyan pesos mexicanos, dólares estadounidenses y euros irlandeses, pero no libras inglesas? ¿Hay alguna razón para que los saltos de palabra no incluyan caracteres de subrayado? –

+1

simplemente NO. Puede poner cualquiera de esos caracteres allí en la matriz. Aunque si lo que buscas es sarcasmo, no creo que se pueda poner allí. –

8

@Zack: Lo publicaré como una respuesta separada.

Aquí hay un ejemplo basado en la publicación de kronoz.

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(); 
} 

// Define other methods and classes here 

// 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");  // Scots 
     ret = properSuffix(ret, "Mac");  // Scots 

     // 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) { 
     if(string.IsNullOrEmpty(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) 
    { 
     List<string> ones = new List<string>() { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; 
     List<string> tens = new List<string>() { "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", "C" }; 
     // assume nobody uses hundreds 

     foreach (string number in ones) 
     { 
      if (word.Equals(number, StringComparison.InvariantCultureIgnoreCase)) 
      { 
       return number; 
      } 
     } 

     foreach (string ten in tens) 
     { 
      foreach (string one in ones) 
      { 
       if (word.Equals(ten + one, StringComparison.InvariantCultureIgnoreCase)) 
       { 
        return ten + one; 
       } 
      } 
     } 

     return word; 
    } 

    private static string capitaliseFirstLetter(string word) { 
     return char.ToUpper(word[0]) + word.Substring(1).ToLower(); 
    } 

} 
+1

Ponemos esto en nuestro sistema de producción.Nos tomó 15 minutos para que uno de nuestros clientes preguntara por qué "Macey" estaba siendo configurado para "MacEy" ... Así que eliminamos esa línea de código en particular y dejamos todo lo demás. ¡Gracias! – NotMe

+0

¡Gracias! De hecho, también conozco a un Macey. Hmm ... Cuando tenga algo de tiempo, voy a raspar la página de Wikipedia con los nombres gaélicos escoceses para todas las palabras de caso de MiXeD, y las agrego. Http://en.wikipedia.org/wiki/List_of_Scottish_Gaelic_surnames – Colin

+1

Esto falla si el romano el número está al lado de un símbolo, como "Sir William III". Cambié DealWithRomanNumerals a este one-liner, que funciona muy bien: '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 (palabra, coincidencia => coincidencia.Value.ToUpperInvariant());' - También filtra el nombre chino común "Xi". – Hannobo

2

Escribí esto hoy para implementar en una aplicación en la que estoy trabajando. Creo que este código es bastante auto explicativo con comentarios. No es 100% exacto en todos los casos, pero manejará la mayoría de tus nombres occidentales fácilmente.

Ejemplos:

mary-jane => Mary-Jane

o'brien => O'Brien

Joël VON WINTEREGG => Joël von Winteregg

jose de la acosta => Jose de la Acosta

El código es extensible en que usted puede añadir cualquier valor de cadena a las matrices en la parte superior para adaptarse tus necesidades. Estudie y agregue cualquier característica especial que pueda ser necesaria.

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); 
} 
0

hice un C# puerto rápido de https://github.com/tamtamchik/namecase, que se basa en Lingua :: ES :: NameCase.

public static class NameCase 
{ 
    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 it's approrpiate 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; 
    } 
} 

Este es mi método de prueba, todo parece pasar OK:

[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); 
     } 

    } 
0

Un montón de buenas respuestas aquí. El mío es bastante simple y solo toma en cuenta los nombres que tenemos en nuestra organización. Puede expandirlo como lo desee. Esta no es una solución perfecta y cambiará Vancouver a VanCouver, lo cual es incorrecto. Así que ajústelo si lo usa.

Aquí estaba mi solución en C#. Esto codifica los nombres en el programa pero con un poco de trabajo puede mantener un archivo de texto fuera del programa y leer en el nombre de excepciones (es decir, Van, Mc, Mac) y recorrerlos.

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 ""; 
} 
0

Sé que este mensaje ha sido abierta por un tiempo, pero como yo estaba haciendo la investigación para este problema me encontré con este sitio ingenioso, que le permite pegar en los nombres que se capitalizan con bastante rapidez: https://dialect.ca/code/name-case/. Quería incluirlo aquí como referencia para otros que realizan investigaciones/proyectos similares.

Liberan el algoritmo que han escrito en php en este enlace: https://dialect.ca/code/name-case/name_case.phps

Una prueba preliminar y la lectura de su código sugiere que han sido bastante completo.

Cuestiones relacionadas