Pregunta

¿Alguien tiene un algoritmo Proper Case o PCase confiable (similar a UCase o Upper)?Estoy buscando algo que tome un valor como "GEORGE BURDELL" o "george burdell" y lo convierte en "George Burdell".

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

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


ACTUALIZAR: Estoy usando C# como lenguaje, pero puedo convertir desde casi cualquier cosa (suponiendo que exista una funcionalidad similar).

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

¿Fue útil?

Solución

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

using System.Globalization;

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

Volverá "George Burdell.Y puedes usar tu propia cultura si hay algunas reglas especiales involucradas.

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

Otros consejos

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

}

También existe este elegante script en Perl para el texto entre mayúsculas y minúsculas.

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 por caso adecuado te refieres...para los nombres de las personas solo.

Escribí esto hoy para implementarlo en una aplicación en la que estoy trabajando.Creo que este código se explica por sí mismo con comentarios.No es 100% preciso en todos los casos, pero manejará fácilmente la mayoría de los nombres occidentales.

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 el sentido de que puede agregar cualquier valor de cadena a las matrices en la parte superior para satisfacer sus necesidades.Estúdielo 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);
}

Hice una adaptación rápida a C# de https://github.com/tamtamchik/namecase, que se basa en Lingua::EN::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;
    }
}

Uso

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

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

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

}

¿Qué lenguaje de programación utilizas?Muchos idiomas permiten funciones de devolución de llamada para coincidencias de expresiones regulares.Estos se pueden utilizar para encajar fácilmente la coincidencia.La expresión regular que se utilizaría es bastante simple, sólo tienes que hacer coincidir todos los caracteres de la palabra, así:

/\w+/

Alternativamente, ya puedes extraer el primer carácter para que sea una coincidencia adicional:

/(\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 entonces simplemente devolver una concatenación de las visitas.En pseudo Python (en realidad no conozco Python):

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

Por cierto, esto también manejaría el caso de “O'Reilly” porque “O” y “Reilly” se compararían por separado y ambos estarían en mayúsculas apropiadas.Sin embargo, existen otros casos especiales que el algoritmo no maneja bien, p.“McDonald's” o en general cualquier palabra apóstrofe.El algoritmo produciría un “Mcdonald'S” para este último.Se podría implementar un tratamiento especial para el apóstrofo, pero eso interferiría con el primer caso.No es posible encontrar una solución teórica perfecta.En la práctica, podría ser útil considerar la longitud de la parte después del apóstrofe.

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

una forma sencilla de poner en mayúscula la primera letra de cada palabra (separada 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 captar el ejemplo de "O'REILLY" que diste Dividir la cadena en ambos espacios y ' no funcionaría, ya que pondría en mayúscula cualquier letra que apareciera después de un apóstrofe, es decir,la s en fred

así que probablemente intentaría algo 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 captar a O'Reilly, O'Clock, O'Donnell, etc. Espero que ayude

Tenga en cuenta que este código no está probado.

Kronoz, gracias.Encontré en tu función que la línea:

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

debo decir

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

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

mejor,

Enrique

Lo uso como controlador de eventos de cuadros de texto con cambios 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;/\-()#$%^&*€!~+=@"
    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

Muchas buenas respuestas aquí.El mío es bastante sencillo y sólo tiene en cuenta los nombres que tenemos en nuestra organización.Puedes ampliarlo como desees.Esta no es una solución perfecta y cambiará Vancouver por VanCouver, lo cual está mal.Así que modifícalo si lo usas.

Aquí estaba mi solución en C#.Esto codifica los nombres en el programa, pero con un poco de trabajo podría mantener un archivo de texto fuera del programa y leer las excepciones de nombres (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 "";
}

Sé que este hilo ha estado abierto por un tiempo, pero mientras investigaba este problema encontré este sitio ingenioso, que le permite pegar nombres para ponerlos en mayúscula 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 una lectura de su código sugieren que han sido bastante minuciosos.

No menciona en qué idioma desea la solución, por lo que aquí hay un pseudocó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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top