Есть ли у кого-нибудь надежный алгоритм правильного регистра или PCase (похожий на UCase или Upper)? Я ищу что-то, что принимает значение, такое как "GEORGE BURDELL" или "george burdell", и превращает его в "George Burdell".
У меня есть простой, который справляется с простыми случаями. Идеальным было бы иметь что-то, что могло бы обрабатывать такие вещи, как "O'REILLY", и превращать его в "O'Reilly", но я знаю, что это сложнее.
Я в основном сосредоточен на английском языке, если это упрощает ситуацию.
Обновлено: Я использую C# в качестве языка, но я могу конвертировать практически из чего угодно (при условии, что подобная функциональность существует).
Я согласен с тем, что сцена из «Макдональдса» сложна. Я хотел упомянуть это вместе с моим примером О'Рейли, но не упомянул в исходном посте.





Какой язык программирования ты используешь? Многие языки допускают функции обратного вызова для совпадений с регулярными выражениями. Их можно использовать для удобного подбора спички. Регулярное выражение, которое будет использоваться, довольно простое, вам просто нужно сопоставить все символы слова, например:
/\w+/
В качестве альтернативы вы уже можете извлечь первый символ, который будет дополнительным совпадением:
/(\w)(\w*)/
Теперь вы можете получить доступ к первому и последующим персонажам в матче по отдельности. Затем функция обратного вызова может просто вернуть конкатенацию совпадений. В псевдо-Python (на самом деле я не знаю Python):
def make_proper(match):
return match[1].to_upper + match[2]
Между прочим, это также относится к случаю «О'Рейли», потому что «О» и «Рейли» будут сопоставляться отдельно и оба должны иметь соответствующий регистр. Однако есть и другие особые случаи, которые не обрабатываются алгоритмом должным образом, например: «Макдональдс» или вообще любое слово с апострофом. Для последнего алгоритм будет производить «Макдональдс». Может быть реализована специальная обработка апострофа, но это повлияет на первый случай. Найти идеальное решение невозможно. На практике это может помочь учесть длину части после апострофа.
простой способ сделать первую букву каждого слова заглавной (через пробел)
$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", который вы привели разделение строки на оба пробела и 'не сработает, так как при этом любая буква, появившаяся после апострафа, будет заглавной, то есть 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);
Это должно поймать О'Рейли, О'Клока, О'Доннелла и т. д. Надеюсь, это поможет
Обратите внимание, что этот код не проверен.
Вы не указываете, на каком языке хотите получить решение, так что вот какой-то псевдокод.
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
Также есть этот изящный 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 }{ . ( defined ? # preserve URL, domain, or email : defined ? "\L" # lowercase small word : defined ? "\u\L" # capitalize word w/o internal caps : # preserve other kinds of word ) . }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 }{\u\L}xig; s{ \b ( $small_re ) # small word... (?= [[:punct:]]* \Z # ... at the end of the title... | ['"’”)\]] [ ] ) # ... or of an inserted subphrase? }{\u\L}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}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:]]+- ) # = first word and hyphen, should already be properly capped ( $small_re ) # ... followed by small word (?! - ) # Negative lookahead for another '-' }{\u}xig; print "$_"; } __END__
Но похоже, что под правильным регистром вы имеете в виду ... для имен людей Только.
Если я не понял ваш вопрос неправильно, я не думаю, что вам нужно сворачивать свои собственные, класс TextInfo может сделать это за вас.
using System.Globalization;
CultureInfo.InvariantCulture.TextInfo.ToTitleCase("GeOrGE bUrdEll")
Вернется "Джордж Берделл. И вы можете использовать свою собственную культуру, если есть какие-то особые правила".
Обновлять:Майкл (в комментарии к этому ответу) указал, что это не сработает, если введены все заглавные буквы, поскольку метод предполагает, что это аббревиатура. Наивный обходной путь для этого - использовать .ToLower () текст перед отправкой его в ToTitleCase.
@Michael: Правильно ... Я думаю, что простой способ избежать этого - убедиться, что ввод будет в нижнем регистре. Я обновлю свой ответ, чтобы отразить это.
InvariantCulture используется для операций, которые требуют культурного компонента, но не соответствуют какой-либо реальной человеческой культуре. Поскольку исходный плакат ориентирован на реальный человеческий язык (английский), необходимо использовать объект культуры, для которого задан английский язык.
Вот, возможно, наивная реализация 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: опубликуйте свою версию как собственный ответ, не редактируйте чей-то ответ так радикально.
Кроноз, спасибо. Я обнаружил в вашей функции, что строка:
`if (!lowerWord.Contains(lowerPrefix)) return word`;
должен сказать
if (!lowerWord.StartsWith(lowerPrefix)) return word;
поэтому "информация" не меняется на "Информация".
Лучший,
Энрике
Я использую это как обработчик событий 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
Есть ли причина, по которой в разрыв слов включаются мексиканские песо, американские доллары и ирландские евро, но не английские фунты? Есть ли причина, по которой разрывы слов не включают символы подчеркивания?
просто НЕТ. Вы можете поместить в массив любой из этих символов. Хотя если это сарказм, то я не думаю, что это можно туда вставлять.
@Zack: Я отправлю это как отдельный ответ.
Вот пример, основанный на сообщении 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();
}
// 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 [Hannobo](https://stackoverflow.com/users/785111/hannobo)
// 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» ... Итак, мы удалили эту конкретную строку кода и оставили все остальное. Спасибо!
Спасибо! На самом деле я тоже знаю Мейси. Хмм ... Когда у меня будет немного времени, я прочищу страницу Википедии о шотландских гэльских именах для всех слов в регистре MiXeD и добавлю их.
Это не удается, если римская цифра находится рядом с символом, например «Сэр Вильгельм 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 => match.Value.ToUpperInvariant()); - также отфильтровывает общее китайское имя «Си».
Я написал это сегодня, чтобы реализовать в приложении, над которым я работаю. Я думаю, что этот код довольно понятен с комментариями. Он не на 100% точен во всех случаях, но он легко справится с большинством ваших западных имен.
Примеры:
mary-jane => Mary-Jane
o'brien => O'Brien
Joël VON WINTEREGG => Joël von Winteregg
jose de la acosta => 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);
}
Я быстро перенес C# на https://github.com/tamtamchik/namecase, который основан на 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"}, // della and delle Italian.
{@"\bD([aeiou])\b" , @"d"}, // da, de, di Italian; du French; do Brasil
{@"\bD([ao]s)\b" , @"d"}, // das, dos Brasileiros
{@"\bDe([lrn])\b" , @"de"}, // 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"}, // 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);
}
}
Выглядит отлично! Кстати, когда я вставляю C# 7, я получаю ошибку Имена элементов не могут совпадать с их включающим типом. Я исправил, просто переименовав класс
Здесь много хороших ответов. Моя довольно проста и учитывает только те имена, которые есть в нашей организации. Вы можете расширять его по своему желанию. Это не идеальное решение и превратит Ванкувер в Ванкувер, что неверно. Так что поправьте его, если вы его используете.
Вот мое решение на 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 "";
}
Я знаю, что эта ветка была открыта некоторое время, но, когда я занимался исследованием этой проблемы, я наткнулся на этот отличный сайт, который позволяет вам довольно быстро вставлять имена с заглавной буквы: https://dialect.ca/code/name-case/. Я хотел включить его сюда для справки для других, занимающихся аналогичными исследованиями / проектами.
Они выпускают алгоритм, который они написали на php по этой ссылке: https://dialect.ca/code/name-case/name_case.phps
Предварительный тест и чтение их кода предполагает, что они были достаточно тщательными.
На самом деле это неверно. В вашем примере будет возвращено "GEORGE BURDELL" Из документации: Как правило, регистр заголовка преобразует первый символ слова в верхний регистр, а остальные символы в нижний регистр. Однако слово, написанное полностью в верхнем регистре, например аббревиатура, не преобразуется.