Sự khác biệt giữa InvariantCARM và so sánh chuỗi thông thường


548

Khi so sánh hai chuỗi trong c # cho đẳng thức, sự khác biệt giữa InvariantCARM và so sánh thông thường là gì?



2
Đối với những người sử dụng String1.Equals(String2, StringComparison.Ordinal), bạn nên sử dụng tốt hơn String1 == String2về bản chất String1.Equals(String2)và mặc định đó là một so sánh phân biệt theo trường hợp thông thường.
Ghasan

3
@Ghasan Không chắc chắn nếu điều đó làm cho =="tốt hơn", nhưng nó là một) ngắn hơn, b) ít rõ ràng hơn về những gì chính xác nó làm và c) String1có thể là null mà không cần so sánh ném a NullReferenceException.
Eugene Beresovsky

3
@Ghasan Các thực tiễn tốt nhất chính thức của MSDN khi sử dụng chuỗi trong trang .NET Framework ( msdn.microsoft.com/en-us/l Library / trộm ) khuyên bạn nên sử dụng quá tải chỉ định rõ ràng StringComparisonloại. Trong trường hợp so sánh chuỗi, nó có nghĩa String.Equals.
Ohad Schneider

3
@EugeneBeresovsky Để tránh NullReferenceExceptionbạn chỉ cần sử dụng phương thức tĩnh : String.Equals(string1, string2, StringComparison.Ordinal).
Ohad Schneider

Câu trả lời:


302

Bất biến

Sử dụng một bộ "tiêu chuẩn" thứ tự ký tự (a, b, c, ... vv). Điều này trái ngược với một số địa phương cụ thể, có thể sắp xếp các ký tự theo các thứ tự khác nhau ('a-with-cấp' có thể trước hoặc sau 'a', tùy thuộc vào miền địa phương, v.v.).

Bình thường

Mặt khác, nhìn hoàn toàn vào các giá trị của byte thô đại diện cho ký tự.


Có một mẫu tuyệt vời tại http://msdn.microsoft.com/en-us/l Library / e6883c06.aspx cho thấy kết quả của các giá trị StringComparison khác nhau. Tất cả các cách ở cuối, nó hiển thị (trích):

StringComparison.InvariantCulture:
LATIN SMALL LETTER I (U+0069) is less than LATIN SMALL LETTER DOTLESS I (U+0131)
LATIN SMALL LETTER I (U+0069) is less than LATIN CAPITAL LETTER I (U+0049)
LATIN SMALL LETTER DOTLESS I (U+0131) is greater than LATIN CAPITAL LETTER I (U+0049)

StringComparison.Ordinal:
LATIN SMALL LETTER I (U+0069) is less than LATIN SMALL LETTER DOTLESS I (U+0131)
LATIN SMALL LETTER I (U+0069) is greater than LATIN CAPITAL LETTER I (U+0049)
LATIN SMALL LETTER DOTLESS I (U+0131) is greater than LATIN CAPITAL LETTER I (U+0049)

Bạn có thể thấy rằng sản lượng InvariantCARM (U + 0069, U + 0049, U + 00131), sản lượng thông thường (U + 0049, U + 0069, U + 00131).


25
So sánh thông thường nhìn vào các điểm mã , không phải các byte.
Joey

144
Tôi cảm thấy như là thông tin hữu ích, nhưng không thực sự trả lời câu hỏi. Khi xác định Bình đẳng của hai chuỗi, có lý do nào để sử dụng InvarintCARM thay vì Ordinal không? Có vẻ như InvariantCARM sẽ được sử dụng để Sắp xếp các chuỗi và Ordinal nên được sử dụng để kiểm tra Bình đẳng (chúng tôi không quan tâm đến việc có dấu - xuất hiện trước hoặc sau một, đơn giản là khác nhau). Mặc dù, bản thân tôi không chắc chắn về điểm này.
MPavlak

18
Xem msdn.microsoft.com/en-us/l Library / ms230117% 28v = vs.90% 29.aspx và lưu ý rằng nên chuẩn hóa chuỗi và so sánh thứ tự.
MPavlak

23
Thông thường nhanh hơn nhiều
Darren

9
Có kết quả kiểm tra hiệu năng tốt được công bố Các thử nghiệm so sánh chuỗi C # cho biết hiệu suất của từng phương pháp so sánh chuỗi khác nhau và thời gian của chúng.
Kumar C

262

Nó không quan trọng, ví dụ - có một thứ gọi là mở rộng nhân vật

var s1 = "Strasse";
var s2 = "Straße";

s1.Equals(s2, StringComparison.Ordinal);           //false
s1.Equals(s2, StringComparison.InvariantCulture);  //true

Với InvariantCulturenhân vật ß được mở rộng thành ss.


1
Có phải điều này cũng khác nhau theo một cách nào đó giữa OrdinalInvariantCulture? Đó là những gì câu hỏi ban đầu là về.
Matthijs Wessels

3
Đối với những người không biết ßthì nên lưu ý rằng ßít nhất là bằng tiếng Đức tương đương với một s kép, Nguồn: en.wikipedia.org/wiki/%C3%9F
Peter

20
Điều đó không hoàn toàn chính xác @Peter, bạn không thể sử dụng ßsshoán đổi cho nhau bằng tiếng Đức (Tôi là người bản ngữ). Có những trường hợp cả hai đều hợp pháp (nhưng thường một trường hợp đã lỗi thời / không được đề xuất) và có những trường hợp chỉ cho phép một hình thức.
enzi

5
Ví dụ đơn giản này cho thấy rõ sự khác biệt giữa 2 so sánh. Tôi nghĩ rằng tôi đang nhận được điều này ngay bây giờ.
BrianLegg

4
Phải thử nó: ideone.com/j8DvDo thật tuyệt! Một bài học nhỏ bằng tiếng Đức là tốt. Tự hỏi sự khác biệt giữa ß và ss bây giờ ...
Mzn

111

Chỉ ra các cách thực hành tốt nhất để sử dụng chuỗi trong .NET Framework :

  • Sử dụng StringComparison.Ordinalhoặc StringComparison.OrdinalIgnoreCaseđể so sánh làm mặc định an toàn của bạn để khớp chuỗi không liên quan đến văn hóa.
  • Sử dụng so sánh với StringComparison.Ordinalhoặc StringComparison.OrdinalIgnoreCasecho hiệu suất tốt hơn.
  • Sử dụng các giá trị phi ngôn ngữ StringComparison.Ordinalhoặc StringComparison.OrdinalIgnoreCasegiá trị thay vì các thao tác chuỗi dựa trên CultureInfo.InvariantCulturethời điểm so sánh không liên quan về mặt ngôn ngữ (ví dụ tượng trưng).

Và cuối cùng:

  • Không sử dụng các hoạt động chuỗi dựa trên StringComparison.InvariantCulturetrong hầu hết các trường hợp . Một trong số ít trường hợp ngoại lệ là khi bạn đang kiên trì dữ liệu có ý nghĩa về mặt ngôn ngữ nhưng mang tính văn hóa.

56

Một sự khác biệt tiện dụng khác (bằng tiếng Anh trong đó các dấu không phổ biến) là so sánh InvariantCARM so sánh toàn bộ các chuỗi bằng cách phân biệt chữ hoa chữ thường, và sau đó nếu cần (và được yêu cầu) phân biệt theo trường hợp sau khi chỉ so sánh trước các chữ cái riêng biệt. (Tất nhiên, bạn cũng có thể thực hiện so sánh không phân biệt chữ hoa chữ thường, không phân biệt theo trường hợp.) Đã sửa:Các chữ cái có dấu được coi là một hương vị khác của cùng một chữ cái và chuỗi được so sánh đầu tiên bỏ qua các dấu và sau đó tính chúng nếu các chữ cái chung khớp với nhau (giống như trường hợp khác nhau ngoại trừ không được bỏ qua trong một so sánh không phân biệt chữ hoa chữ thường). Các nhóm này nhấn các phiên bản của cùng một từ gần nhau thay vì tách biệt hoàn toàn ở điểm khác biệt trọng âm đầu tiên. Đây là thứ tự sắp xếp mà bạn thường tìm thấy trong một từ điển, với các từ viết hoa xuất hiện ngay bên cạnh các chữ cái viết thường và các chữ cái có dấu nằm gần chữ cái không có chữ tương ứng.

Một so sánh thứ tự so sánh nghiêm ngặt về các giá trị ký tự số, dừng lại ở sự khác biệt đầu tiên. Các loại chữ viết hoa này hoàn toàn tách biệt với các chữ cái viết thường (và các chữ cái có dấu có lẽ tách biệt với các chữ cái), vì vậy các từ viết hoa sẽ không sắp xếp gần các chữ cái viết thường.

InvariantCARM cũng coi chữ viết hoa lớn hơn chữ thường, trong khi Ordinal coi chữ viết hoa nhỏ hơn chữ thường (chữ viết tắt của ASCII từ ngày trước khi máy tính có chữ thường, chữ in hoa được phân bổ trước và do đó có giá trị thấp hơn chữ cái viết thường thêm vào sau).

Ví dụ: bởi Ordinal: "0" < "9" < "A" < "Ab" < "Z" < "a" < "aB" < "ab" < "z" < "Á" < "Áb" < "á" < "áb"

Và bởi InvariantCARM: "0" < "9" < "a" < "A" < "á" < "Á" < "ab" < "aB" < "Ab" < "áb" < "Áb" < "z" < "Z"


Tôi đã có cái nhìn khác về điều này và nhận thấy sự không nhất quán giữa ví dụ InvariantCARM và lời giải thích của tôi về việc xử lý các ký tự có dấu. Ví dụ có vẻ đúng, vì vậy tôi đã sửa lời giải thích cho phù hợp. So sánh InvariantCARM không dừng lại ở dấu trọng âm khác nhau đầu tiên và dường như chỉ xem xét một sự khác biệt trọng âm trên cùng một chữ cái nếu phần còn lại của chuỗi khớp với dấu và trường hợp. Một sự khác biệt trọng âm sau đó được xem xét trước một sự khác biệt trường hợp trước đó, vì vậy "Aaba" <"aába".
Rob Parker

31

Mặc dù câu hỏi là về sự bình đẳng , để tham khảo trực quan nhanh chóng, ở đây thứ tự của một số chuỗi được sắp xếp bằng cách sử dụng một vài nền văn hóa minh họa một số đặc điểm riêng ngoài kia.

Ordinal          0 9 A Ab a aB aa ab ss Ä Äb ß ä äb      
IgnoreCase       0 9 a A aa ab Ab aB ss ä Ä äb Äb ß      
--------------------------------------------------------------------
InvariantCulture 0 9 a A  ä Ä aa ab aB Ab äb Äb ss ß     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ß ss     
--------------------------------------------------------------------
da-DK            0 9 a A  ab aB Ab ss ß ä Ä äb Äb aa     
IgnoreCase       0 9 A a  Ab aB ab ß ss Ä ä Äb äb aa     
--------------------------------------------------------------------
de-DE            0 9 a A  ä Ä aa ab aB Ab äb Äb ß ss     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ss ß     
--------------------------------------------------------------------
en-US            0 9 a A  ä Ä aa ab aB Ab äb Äb ß ss     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ss ß     
--------------------------------------------------------------------
ja-JP            0 9 a A  ä Ä aa ab aB Ab äb Äb ß ss     
IgnoreCase       0 9 A a  Ä ä aa Ab aB ab Äb äb ss ß     

Quan sát:

  • de-DE, ja-JPen-USsắp xếp theo cùng một cách
  • Invariantchỉ sắp xếp ssßkhác biệt với ba nền văn hóa trên
  • da-DK sắp xếp khá khác nhau
  • các IgnoreCasevấn đề cờ cho tất cả các nền văn hóa lấy mẫu

Mã được sử dụng để tạo bảng trên:

var l = new List<string>
    { "0", "9", "A", "Ab", "a", "aB", "aa", "ab", "ss", "ß",
      "Ä", "Äb", "ä", "äb", "あ", "ぁ", "ア", "ァ", "A", "亜" };

foreach (var comparer in new[]
{
    StringComparer.Ordinal,
    StringComparer.OrdinalIgnoreCase,
    StringComparer.InvariantCulture,
    StringComparer.InvariantCultureIgnoreCase,
    StringComparer.Create(new CultureInfo("da-DK"), false),
    StringComparer.Create(new CultureInfo("da-DK"), true),
    StringComparer.Create(new CultureInfo("de-DE"), false),
    StringComparer.Create(new CultureInfo("de-DE"), true),
    StringComparer.Create(new CultureInfo("en-US"), false),
    StringComparer.Create(new CultureInfo("en-US"), true),
    StringComparer.Create(new CultureInfo("ja-JP"), false),
    StringComparer.Create(new CultureInfo("ja-JP"), true),
})
{
    l.Sort(comparer);
    Console.WriteLine(string.Join(" ", l));
}

1
Hmmm - OK, thật tuyệt khi bạn đã thực hiện nghiên cứu này và đăng những phát hiện của bạn, mặc dù tôi không chắc chính xác quan điểm của bạn là gì. Dù sao, tiếng Đan Mạch có thể không phải là một trong những "nền văn hóa quan trọng nhất" (mặc dù 5 triệu người Đan Mạch thực sự khá thích văn hóa của họ), nhưng nếu bạn ném "aa" vào như một chuỗi thử nghiệm bổ sung và "da-DK" một nền văn hóa thử nghiệm bổ sung, bạn sẽ thấy một số kết quả thú vị.
RenniePet

1
@RenniePet Cảm ơn vì điều đó. Tôi đã thêm tiếng Đan Mạch, vì nó sắp xếp khá khác so với 3 nền văn hóa khác được sử dụng. (Vì các biểu tượng cảm xúc biểu thị sự mỉa mai dường như không được hiểu rõ trong web đọc tiếng Anh như tôi đã giả định, tôi đã xóa nhận xét "văn hóa quan trọng nhất". Sau tất cả, BCL không có tính năng CultureComparermà chúng ta có thể sử dụng để xác minh. Đối với bảng này, Danishvăn hóa (thông tin) hóa ra rất quan trọng.)
Eugene Beresovsky

1
Cảm ơn. Tôi đã nhận ra rằng nhận xét "văn hóa quan trọng nhất" của bạn được dự định sử dụng bằng một hạt muối - chỉ là tôi đã quá già để sử dụng biểu tượng cảm xúc. Tôi cho rằng việc nhắn tin đã trở nên phổ biến đến mức sử dụng biểu tượng cảm xúc giống như giải thích những câu chuyện cười của bạn sau khi bạn nói với họ, bất kể có ai cười hay không. Ngẫu nhiên, các nền văn hóa Scandinavia khác (Phần Lan, Na Uy và Thụy Điển) giống như Đan Mạch, ngoại trừ cách xử lý rất đặc biệt của "aa" - tất nhiên chứng minh rằng Đan Mạch là văn hóa ưu việt.
RenniePet

1
Đối với những gì nó có giá trị, Đan Mạch sắp xếp ä và aa khác nhau vì vị trí của các chữ cái đặc biệt (ae), ø (oe, ö) và å (aa, ä) ở cuối bảng chữ cái theo thứ tự viết.
Alrekr


5

Dưới đây là một ví dụ trong đó so sánh bình đẳng chuỗi bằng InvariantCARMIgnoreCase và OrdinalIgnoreCase sẽ không cho kết quả tương tự:

string str = "\xC4"; //A with umlaut, Ä
string A = str.Normalize(NormalizationForm.FormC);
//Length is 1, this will contain the single A with umlaut character (Ä)
string B = str.Normalize(NormalizationForm.FormD);
//Length is 2, this will contain an uppercase A followed by an umlaut combining character
bool equals1 = A.Equals(B, StringComparison.OrdinalIgnoreCase);
bool equals2 = A.Equals(B, StringComparison.InvariantCultureIgnoreCase);

Nếu bạn chạy cái này, bằng1 sẽ là sai và bằng2 sẽ là đúng.


Chỉ cần thêm một ví dụ tương tự khác nhưng với chuỗi ký tự, nếu a="\x00e9"(e cấp tính) và b="\x0065\x0301"(e kết hợp với dấu trọng âm), StringComparer.Ordinal.Equals(a, b)sẽ trả về false trong khi StringComparer.InvariantCulture.Equals(a, b)sẽ trả về true.
George Helyar

2

Không cần phải sử dụng exicode char exmap để hiển thị sự khác biệt. Đây là một ví dụ đơn giản mà tôi phát hiện ra hôm nay thật đáng ngạc nhiên, chỉ bao gồm các ký tự ASCII.

Theo bảng ASCII, 0(0x48) nhỏ hơn _(0x95) khi so sánh thông thường. InvariantCARM sẽ nói ngược lại (mã PowerShell bên dưới):

PS> [System.StringComparer]::Ordinal.Compare("_", "0")
47
PS> [System.StringComparer]::InvariantCulture.Compare("_", "0")
-1

-7

Luôn cố gắng sử dụng InvariantCARM trong các phương thức chuỗi chấp nhận nó là quá tải. Bằng cách sử dụng InvariantCARM, bạn sẽ an toàn. Nhiều lập trình viên .NET có thể không sử dụng chức năng này nhưng nếu phần mềm của bạn sẽ được sử dụng bởi các nền văn hóa khác nhau, InvariantCARM là một tính năng cực kỳ tiện dụng.


3
Nếu phần mềm của bạn sẽ không được sử dụng bởi các nền văn hóa khác nhau, thì nó sẽ chậm hơn nhiều so với Ordinal.
Kyle

4
Tôi đã cân nhắc việc hạ thấp vì bạn chắc chắn đã không nghĩ qua phản ứng ngớ ngẩn của mình. Mặc dù bên trong nó là một hạt của sự thật. NẾU ứng dụng của bạn được phổ biến rộng rãi giữa nhiều nền văn hóa ... Điều đó chắc chắn không đảm bảo những lời mở đầu của bạn về "Luôn cố gắng sử dụng InvariantCARM", phải không? Tôi ngạc nhiên khi bạn không quay trở lại trong nhiều năm để chỉnh sửa sự điên rồ này sau khi nhận được một downvote, và có lẽ nhiều kinh nghiệm hơn.
Suamere
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.