InvariantCulture와 서수 문자열 비교의 차이점


548

c #에서 두 문자열이 동일한 지 비교할 때 InvariantCulture와 서수 비교의 차이점은 무엇입니까?



2
를 사용하는 사람들 은 본질적으로 String1.Equals(String2, StringComparison.Ordinal)사용 String1 == String2하는 것이 더 좋으며 String1.Equals(String2)기본적으로 대소 문자를 구분하는 비교입니다.
가산

3
@Ghasan ==그것이 "더 나은" 것인지 확실하지 않지만, a) 더 짧고, b) 정확히 무엇을하는지에 대해 덜 명확하고 c) String1a을 던지는 비교없이 널이 될 수 있습니다 NullReferenceException.
Eugene Beresovsky

3
@Ghasan .NET Framework 페이지 ( msdn.microsoft.com/en-us/library/… )의 문자열 사용을위한 공식 MSDN Best PracticesStringComparison형식 을 명시 적으로 지정하는 오버로드 사용을 권장합니다 . 문자열 비교의 경우 의미 String.Equals합니다.
하드 슈나이더

3
@EugeneBeresovsky NullReferenceException당신은 간단하게 정적 메소드를 사용할 수 있습니다 : String.Equals(string1, string2, StringComparison.Ordinal).
오핫 슈나이더

답변:


302

불변의 문화

"표준"문자 순서 세트 (a, b, c, ... 등)를 사용합니다. 이것은 ( 'A-과 급성'전에 할 수있다 다른 순서의 문자를 정렬 할 수 있습니다 일부 특정 로케일, 대조적이다 또는 'A'는 지역에 따라, 등등 후).

서수

반면에 문자를 나타내는 원시 바이트 값을 순전히 살펴 봅니다.


http://msdn.microsoft.com/en-us/library/e6883c06.aspx 에는 다양한 StringComparison 값의 결과를 보여주는 훌륭한 샘플이 있습니다. 끝까지, 그것은 보여줍니다 (발췌) :

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)

InvariantCulture 수율 (U + 0069, U + 0049, U + 00131), 서수 수율 (U + 0049, U + 0069, U + 00131)이있는 곳을 확인할 수 있습니다.


25
서수 비교는 바이트가 아닌 코드 포인트 를 확인합니다.
Joey

144
유용한 정보 인 것 같지만 실제로 질문에 대답하지는 않습니다. 두 문자열의 동등성 을 결정할 때 서수 대신 InvarintCulture를 사용해야하는 이유가 있습니까? InvariantCulture는 문자열 을 정렬 하는 데 사용되고 Ordinal은 평등 검사에 사용해야합니다 (우리는 악센트가 a가 전후에 오는 것을 신경 쓰지 않습니다. 단순히 다릅니다). 그러나 나는이 점을 조금 확신하지 못한다.
MPavlak

18
msdn.microsoft.com/en-us/library/ms230117%28v=vs.90%29.aspx를 참조 하고 문자열 정규화 및 서수 비교가 권장됩니다.
MPavlak

23
서수는 훨씬 빠릅니다
Darren

9
C # 문자열 비교 테스트 (C # String Comparision Tests) 라는 우수한 성능 테스트 결과 가 나와 있으며, 각 문자열 비교 방법의 성능과 시간을 알려줍니다.
Kumar C

262

예를 들어, 캐릭터 확장이라는 것이 있습니다.

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

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

함께 InvariantCulture친위대 문자 SS로 확대됩니다.


1
이 일 또한 사이에 어떤 방식에서 차이가 있나요 OrdinalInvariantCulture? 그것이 원래의 질문에 관한 것입니다.
Matthijs Wessels

3
모르는 사람들을 위해 ß그것을 주목해야한다 ß독일어 같음 적어도 두 배의, 소스에 : en.wikipedia.org/wiki/%C3%9F
피터

20
@Peter는 정확하지 않습니다 . 독일어로 사용 ß하고 ss상호 교환 할 수 없습니다 (나는 원어민입니다). 둘 다 합법적 인 경우가 있지만 (종종 구식이거나 권장하지 않음) 한 가지 형식 만 허용되는 경우가 있습니다.
enzi

5
이 간단한 예는 두 비교의 차이점을 분명히 보여줍니다. 나는 지금 이것을 얻고 있다고 생각합니다.
BrianLegg

4
시도해 보았습니다 : ideone.com/j8Dv 너무 멋져요! 독일어로도 조금 배울 수 있습니다. ß와 ss의 차이점이 무엇인지 궁금해 ...
Mzn

111

가리키며 은 .NET Framework의 문자열을 사용하기위한 모범 사례 :

  • 문화권에 구애받지 않는 문자열 일치의 안전한 기본값으로 StringComparison.Ordinal또는 StringComparison.OrdinalIgnoreCase비교에 사용하십시오 .
  • 더 나은 성능을 위해 StringComparison.Ordinal또는 StringComparison.OrdinalIgnoreCase더 나은 성능을 위해 비교를 사용하십시오 .
  • 비교가 언어 적으로 관련이없는 경우 (예 : 상징적)를 기준으로 문자열 조작 대신 비 언어 StringComparison.Ordinal또는 StringComparison.OrdinalIgnoreCase값을 사용하십시오 CultureInfo.InvariantCulture.

그리고 마지막으로:

  • StringComparison.InvariantCulture대부분의 경우를 기준으로 문자열 연산을 사용하지 마십시오 . 몇 가지 예외 중 하나는 언어 적으로 의미가 있지만 문화적으로 불가지론적인 데이터를 유지하는 경우입니다.

56

또 다른 편리한 차이점은 (영어에서 악센트가 드문 경우) InvariantCulture 비교는 전체 문자열을 대소 문자를 구분하지 않고 먼저 비교 한 다음 필요에 따라 (필요한 경우) 구별 문자 만 비교 한 후 대소 문자를 구분한다는 것입니다. (대소 문자를 구분하지 않는 대소 문자 구분 비교도 가능합니다.) 대소 문자를 구분하지 않습니다 .악센트 문자는 동일한 문자의 또 다른 풍미로 간주되며 문자열은 먼저 악센트를 무시한 다음 일반 문자가 모두 일치하는 경우 해당 문자를 고려합니다 (대소 문자를 구분하지 않는 비교에서 궁극적으로 무시되지 않는 경우를 제외하고는 대소 문자가 다름). 이 단어들은 첫 번째 억양 차이에서 완전히 분리되지 않고 서로 가까이있는 다른 단어의 억양 버전을 그룹화합니다. 이것은 사전에서 일반적으로 찾을 수있는 정렬 순서이며, 대문자로 된 단어는 소문자와 바로 옆에 나타나고 악센트 부호 문자는 해당하는 액센트가없는 문자 근처에 있습니다.

서수 비교는 숫자 값을 엄격하게 비교하여 첫 번째 차이에서 멈 춥니 다. 이렇게하면 대문자와 소문자가 완전히 분리되어 (대소 문자와 악센트 문자가 구분됨) 대문자로 된 단어는 소문자와 거의 일치하지 않습니다.

InvariantCulture는 또한 대문자가 소문자보다 큰 것으로 간주하는 반면, Ordinal은 대문자가 소문자보다 작은 것으로 간주합니다 (컴퓨터는 소문자를 사용하기 전 예전의 ASCII 보류, 대문자가 먼저 할당되어 소문자보다 낮은 값을 가짐) 나중에 추가).

예를 들어, 서수 : "0" < "9" < "A" < "Ab" < "Z" < "a" < "aB" < "ab" < "z" < "Á" < "Áb" < "á" < "áb"

그리고 InvariantCulture에 의해 : "0" < "9" < "a" < "A" < "á" < "Á" < "ab" < "aB" < "Ab" < "áb" < "Áb" < "z" < "Z"


나는 이것을 다시보고 InvariantCulture 예제와 악센트 문자 처리에 대한 설명 사이에 불일치가 있음을 알았습니다. 예제가 올바른 것처럼 보이므로 설명을 일관성있게 수정했습니다. InvariantCulture 비교는 첫 번째 다른 악센트에서 멈추지 않으며 나머지 문자열이 악센트 및 대소 문자 외에 일치하는 경우 동일한 문자의 악센트 차이 만 고려하는 것으로 보입니다. 그런 다음 "Aaba"< "aába"와 같이 이전의 차이보다 악센트 차이가 고려됩니다.
Rob Parker

31

문제는 평등 에 관한 것이지만, 빠른 시각적 참조를 위해 여기서 일부 특질을 보여주는 몇 가지 문화를 사용하여 일부 문자열의 순서를 정렬 했습니다.

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 ß     

관찰 :

  • de-DE, ja-JP그리고 en-US정렬 같은 방법으로
  • Invariant단지 종류 ssß다르게 위의 세 가지 문화
  • da-DK 상당히 다르게 정렬
  • IgnoreCase모든 샘플링 문화에 대한 플래그 사항

위의 테이블을 생성하는 데 사용되는 코드 :

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
흠-좋아, 당신의 요점이 정확히 확실하지는 않지만, 당신이이 연구를하고 결과를 게시 한 것이 기쁘다. 어쨌든 덴마크어는 "가장 중요한 문화"중 하나가 아닐 수도 있지만 (5 백만 명의 덴마크 인은 실제로 문화를 좋아하지만) "aa"를 추가 테스트 문자열로, "da-DK"를 추가 테스트 문화, 당신은 몇 가지 흥미로운 결과를 볼 수 있습니다.
RenniePet

1
@RenniePet 감사합니다. 나는 사용 된 3 가지 다른 문화와는 상당히 다른 덴마크어를 추가했습니다. (아이러니를 나타내는 이모티콘은 내가 생각했던 것처럼 영어 독해 웹에서 잘 이해되지 않는 것 같아서 "가장 중요한 문화"의견을 제거했습니다. 결국 BCL에는 CultureComparer사용할 수있는 기능이 없습니다. 이 표에서 Danish문화 (정보)는 매우 중요한 것으로 밝혀졌습니다.)
Eugene Beresovsky

1
감사. 나는 당신의 "가장 중요한 문화"의견이 한 알의 소금으로 만들어 졌다는 것을 깨달았습니다. 이모티콘을 사용하기에는 너무 오래 된 것입니다. 나는 문자 메시지가 너무 보편적이어서 이모티콘을 사용하는 것이 누군가의 웃음 여부에 관계없이 말한 후에 농담을 설명하는 것과 비슷하다고 생각합니다. 또한 다른 스칸디나비아 문화 (핀란드어, 노르웨이어 및 스웨덴어)는 "aa"를 매우 특별하게 처리한다는 점을 제외하고는 덴마크어와 동일합니다. 이는 덴마크어가 물론 우수한 문화임을 증명합니다.
RenniePet

1
가치가있는 것을 위해, 덴마크어는 알파벳의 끝에있는 문자 (æ), ø (oe, ö) 및 å (aa, ä)의 위치 때문에 서면 순서대로 ä와 aa를 다르게 정렬합니다.
Alrekr


5

다음은 InvariantCultureIgnoreCase와 OrdinalIgnoreCase를 사용한 문자열 동등 비교가 동일한 결과를 제공하지 않는 예입니다.

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

이를 실행하면 equals1은 false가되고 equals2는 true가됩니다.


다른 유사한 예제를 추가하지만 문자열 리터럴을 사용하면 a="\x00e9"(ecute) 및 b="\x0065\x0301"(e a 급성 악센트와 결합 된 경우) StringComparer.Ordinal.Equals(a, b)false를 StringComparer.InvariantCulture.Equals(a, b)반환하고 true를 반환합니다.
George Helyar

2

차이점을 보여주기 위해 멋진 유니 코드 문자 exmaples를 사용할 필요가 없습니다. 오늘 내가 찾은 간단한 예가 ASCII 문자로만 구성된 놀라운 예입니다.

ASCII 테이블에 따르면, 0(0x48)은 _보통 비교할 때 (0x95) 보다 작습니다 . InvariantCulture는 반대라고 말합니다 (아래 PowerShell 코드).

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

-7

항상 InvariantCulture를 오버로드로 허용하는 문자열 메소드에서 사용하십시오. InvariantCulture를 사용하면 안전합니다. 많은 .NET 프로그래머가이 기능을 사용하지 않을 수도 있지만 다른 문화권에서 소프트웨어를 사용할 경우 InvariantCulture는 매우 편리한 기능입니다.


3
다른 문화권에서 소프트웨어를 사용하지 않으면 서수보다 훨씬 느립니다.
Kyle

4
우연히 당신의 우연한 반응을 생각하지 않았기 때문에 하향 조정을 고려했습니다. 그 안에는 진실이 있습니다. 응용 프로그램이 여러 문화에 널리 퍼져 있다면 ... "항상 InvariantCulture를 사용하려고합니다"라는 첫 마디를 보증하지 않습니까? 공감대를 경험 한 후 더 많은 경험을 한 후에이 수수께끼를 편집하기 위해 수년 동안 돌아 오지 않은 것에 놀랐습니다.
Suamere
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.