2008-08-28 32 views
23

Có ai có thuật toán đúng trường hợp hoặc PCase đáng tin cậy (tương tự như UCase hoặc Upper) không? Tôi đang tìm một thứ có giá trị như "GEORGE BURDELL" hoặc "george burdell" và biến nó thành "George Burdell".Có ai có thuật toán Trường hợp phù hợp hay không

Tôi có một cách đơn giản để xử lý các trường hợp đơn giản. Lý tưởng sẽ là có thứ gì đó có thể xử lý những thứ như "O'REILLY" và biến nó thành "O'Reilly", nhưng tôi biết đó là khó khăn hơn.

Tôi chủ yếu tập trung vào ngôn ngữ tiếng Anh nếu điều đó đơn giản hóa mọi thứ.


UPDATE: Tôi đang sử dụng C# là ngôn ngữ, nhưng tôi có thể chuyển đổi từ hầu hết mọi thứ (giả sử như chức năng tồn tại).

Tôi đồng ý rằng scneario của McDonald là một khó khăn. Tôi có đề cập đến rằng cùng với ví dụ O'Reilly của tôi, nhưng không có trong bài gốc.

Trả lời

18

Trừ khi tôi đã hiểu lầm câu hỏi của bạn Tôi không nghĩ rằng bạn cần phải cuộn của riêng bạn, lớp TextInfo có thể làm điều đó cho bạn.

using System.Globalization; 

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

Sẽ trở lại "George Burdell Và bạn có thể sử dụng văn hóa của riêng bạn nếu có một số quy tắc đặc biệt liên quan đến

Cập nhật:..Michael (trong một bình luận cho câu trả lời này) chỉ ra rằng điều này sẽ không làm việc nếu đầu vào là tất cả các mũ từ phương pháp này sẽ giả định rằng nó là một từ viết tắt. Cách giải quyết ngây thơ cho điều này là để .ToLower() văn bản trước khi gửi nó đến ToTitleCase

+1

Thực ra, điều này là không chính xác. Ví dụ bạn sẽ trả về "GEORGE BURDELL" Từ tài liệu: Nói chung, vỏ tiêu đề chuyển đổi ký tự đầu tiên của từ thành chữ hoa và phần còn lại của ký tự thành chữ thường. Tuy nhiên, một từ hoàn toàn viết hoa, chẳng hạn như từ viết tắt, không được chuyển đổi. –

+0

@Michael: Đúng vậy ... Tôi đoán cách đơn giản để tránh điều đó là đảm bảo đầu vào thấp hơn để bắt đầu. Tôi sẽ cập nhật câu trả lời của tôi để phản ánh điều này. –

+0

InvariantCulture được sử dụng cho các hoạt động đòi hỏi một thành phần văn hóa nhưng không phù hợp với bất kỳ văn hóa thực tế của con người. Vì poster gốc được tập trung vào một ngôn ngữ thực tế của con người (tiếng Anh), nó là cần thiết để sử dụng một đối tượng văn hóa được thiết lập để tiếng Anh. –

1

Bạn sử dụng ngôn ngữ lập trình nào? Nhiều ngôn ngữ cho phép chức năng gọi lại cho các đối sánh cụm từ thông dụng. Đây có thể được sử dụng để khớp nối một cách dễ dàng. Các biểu hiện thường xuyên sẽ được sử dụng khá đơn giản, bạn chỉ cần có để phù hợp với tất cả các ký tự chữ, như vậy:

/\w+/ 

Ngoài ra, bạn đã có thể trích xuất các ký tự đầu tiên là một trận đấu thêm:

/(\w)(\w*)/ 

Bây giờ bạn có thể truy cập vào ký tự đầu tiên và các ký tự kế tiếp trong trận đấu một cách riêng biệt. Hàm gọi lại sau đó có thể đơn giản trả về một kết nối của các lần truy cập. Trong giả Python (tôi không thực sự biết Python):

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

Ngẫu nhiên, điều này cũng sẽ xử lý trường hợp của “O'Reilly” bởi vì “O” và “Reilly” sẽ được xuất hiện riêng biệt và cả hai propercased. Tuy nhiên, có những trường hợp đặc biệt khác không được xử lý tốt bằng thuật toán, ví dụ: “McDonald's” hoặc nói chung là từ bất kỳ. Thuật toán sẽ tạo ra "Mcdonald'S" cho cái sau. Một xử lý đặc biệt cho dấu nháy đơn có thể được thực hiện nhưng điều đó sẽ can thiệp vào trường hợp đầu tiên. Tìm kiếm một giải pháp hoàn hảo có thể xảy ra là không thể. Trong thực tế, nó có thể giúp xem xét độ dài của phần sau dấu nháy đơn.

0

một cách đơn giản để tận dụng các chữ cái đầu tiên của mỗi từ (tách bởi một không gian)

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

về bắt "O'Reilly" Ví dụ bạn đã tách chuỗi trên cả không gian và ' sẽ không làm việc vì nó sẽ tận dụng bất kỳ bức thư đó xuất hiện sau một apostraphe tức là s trong

vì vậy tôi có lẽ sẽ thử một cái gì đó của Fred như

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

Điều này sẽ bắt O'Reilly, O'Clock, O'Donnell vv hy vọng nó sẽ giúp

Xin lưu ý mã này chưa được kiểm tra.

-1

Bạn không đề cập đến ngôn ngữ nào bạn muốn giải pháp vì vậy đây là một số mã giả.

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

Ngoài ra còn có tập lệnh Perl gọn gàng này cho văn bản tiêu đề.

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__ 

Nhưng có vẻ như bởi trường hợp thích hợp, bạn có nghĩa là .. cho tên người chỉ.

1

Đây là một triển khai C# có thể ngây thơ: -

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: đăng phiên bản của bạn làm câu trả lời riêng, không chỉnh sửa câu trả lời của người khác một cách triệt để. – zwol

0

Kronoz, cảm ơn. Tôi tìm thấy trong chức năng của bạn mà dòng:

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

phải nói

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

như vậy "Información" không được đổi thành "Información"

tốt nhất,

Enrique

0

Tôi sử dụng điều này như là xử lý sự kiện textchanged của hộp văn bản. Mục nhập hỗ trợ của "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

Có lý do nào để nghỉ giải lao bao gồm peso Mexico, đô la Mỹ và đồng euro Ailen, chứ không phải bảng Anh? Có lý do nào để ngắt từ không bao gồm dấu gạch dưới không? –

+1

đơn giản là KHÔNG. Bạn có thể đặt bất kỳ ký tự nào trong đó trong mảng. Mặc dù nếu nó mỉa mai bạn đang sau khi tôi không nghĩ rằng nó có thể được đặt trong đó. –

8

@Zack: Tôi sẽ đăng nó dưới dạng trả lời riêng.

Đây là ví dụ dựa trên bài đăng của 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

Chúng tôi đưa điều này vào hệ thống sản xuất của mình.Phải mất tất cả 15 phút cho một trong những khách hàng của chúng tôi để hỏi tại sao "Macey" đã được thiết lập để "MacEy" ... Vì vậy, chúng tôi loại bỏ dòng mã cụ thể và để lại tất cả mọi thứ khác. Cảm ơn! – NotMe

+0

Cảm ơn! Thực ra tôi cũng biết Macey. Hmm ... Khi tôi nhận được một thời gian tôi sẽ cạo trang Wikipedia về tên Gaelic của Scotland cho tất cả các từ trường hợp của MiXeD và thêm những từ đó vào. Http://en.wikipedia.org/wiki/List_of_Scottish_Gaelic_surnames – Colin

+1

Điều này không thành công nếu chữ La Mã chữ số bên cạnh một biểu tượng, như "Sir William III". Tôi đã thay đổi DealWithRomanNumerals thành một lớp lót, hoạt động tuyệt vời: 'trả về Regex mới (@" \ 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 (từ, match => match.Value.ToUpperInvariant());' - Ngoài ra lọc ra tên tiếng Trung phổ biến "Xi". – Hannobo

2

Tôi đã viết ngày hôm nay để triển khai trong ứng dụng tôi đang làm việc. Tôi nghĩ rằng mã này là khá tự giải thích với ý kiến. Nó không chính xác 100% trong mọi trường hợp nhưng nó sẽ xử lý hầu hết các tên phương Tây của bạn một cách dễ dàng.

Ví dụ:

mary-jane => Mary-Jane

o'brien => O'Brien

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

jose de la acosta => Jose de la Acosta

Mã này có thể mở rộng trong đó bạn có thể thêm bất kỳ giá trị chuỗi các mảng ở phía trên để phù hợp với bạn cần. Vui lòng nghiên cứu và thêm bất kỳ tính năng đặc biệt nào có thể được yêu cầu.

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

Tôi đã thực hiện cổng C# nhanh chóng là https://github.com/tamtamchik/namecase, dựa trên Lingua :: EN :: 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; 
    } 
} 

này được phương pháp thử nghiệm của tôi, mọi thứ dường như vượt qua 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

Rất nhiều câu trả lời tốt ở đây. Mỏ khá đơn giản và chỉ tính đến tên chúng tôi có trong tổ chức của mình. Bạn có thể mở rộng nó như bạn muốn. Đây không phải là một giải pháp hoàn hảo và sẽ thay đổi vancouver để VanCouver, đó là sai. Vì vậy, tinh chỉnh nó nếu bạn sử dụng nó.

Đây là giải pháp của tôi trong C#. Điều này mã hóa các tên trong chương trình nhưng với một công việc nhỏ, bạn có thể giữ một tệp văn bản bên ngoài chương trình và đọc trong các ngoại lệ tên (ví dụ: Van, Mc, Mac) và lặp lại chúng.

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

Tôi biết chủ đề này đã được mở cho một lúc, nhưng như tôi đã thực hiện nghiên cứu cho vấn đề này tôi đã xem qua trang web này tiện lợi, cho phép bạn để dán vào tên được vốn hóa khá nhanh chóng: https://dialect.ca/code/name-case/. Tôi muốn bao gồm nó ở đây để tham khảo cho những người khác làm nghiên cứu/dự án tương tự.

Họ phát hành các thuật toán họ đã viết bằng php tại liên kết này: https://dialect.ca/code/name-case/name_case.phps

Một thử nghiệm sơ bộ và đọc mã của họ cho thấy họ đã khá kỹ lưỡng.

Các vấn đề liên quan