web-dev-qa-db-ja.com

InvariantCultureと序数文字列比較の違い

C#の2つの文字列が等しいかどうかを比較する場合、InvariantCultureと序数比較の違いは何ですか?

491
Kapil

InvariantCulture

「標準」の文字順序(a、b、cなど)を使用します。これは、異なる順序で文字をソートする可能性がある特定のロケールとは対照的です( 'a-with-acute'はまたはロケールに応じて「a」の後など)。

序数

一方、文字を表す生のバイトの値を純粋に調べます。


さまざまなStringComparison値の結果を示すすばらしいサンプルが http://msdn.Microsoft.com/en-us/library/e6883c06.aspx にあります。最後までずっと、それは(抜粋)を示しています。

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)が得られることがわかります。

274
JaredReisinger

例えば問題ではありません - 文字拡張というものがあります

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

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

InvariantCultureでは、ß文字はssに展開されます。

210

を指す .NET Frameworkで文字列を使用するためのベストプラクティス

  • カルチャにとらわれない文字列マッチングの安全なデフォルトとして、比較にはStringComparison.OrdinalまたはStringComparison.OrdinalIgnoreCaseを使用してください。
  • パフォーマンスを向上させるには、StringComparison.OrdinalまたはStringComparison.OrdinalIgnoreCaseとの比較を使用してください。
  • 比較が言語的に無関係の場合(例:記号)、StringComparison.Ordinalに基づく文字列操作の代わりに、非言語的なStringComparison.OrdinalIgnoreCaseまたはCultureInfo.InvariantCulture値を使用します。

そして最後に:

  • ほとんどの場合、StringComparison.InvariantCultureに基づく文字列操作は使用しないでください。いくつかの例外の1つは、言語的には意味があるが文化的に不可知論的なデータを保持している場合です。
85
Dariusz

もう1つの便利な違い(アクセント記号が一般的でない英語)は、InvariantCultureの比較では最初に大文字と小文字が区別されずに文字列全体が比較され、次に必要に応じて要求されます。大文字と小文字を区別しないで比較することもできます(もちろん大文字と小文字は区別されません)。修正済みアクセント付き文字は同じ文字の別のフレーバーと見なされ、文字列が最初に比較されます。アクセント記号を無視し、一般文字がすべて一致する場合はそれらを考慮に入れます(大/小文字を区別しない比較では、最終的に無視されない点を除いて、大文字と小文字が区別されます)。これは、最初のアクセントの違いで完全に分離されているのではなく、他の点では同じ単語のアクセント付きバージョンを互いに近くにグループ化します。これは辞書で通常見られるソート順です。大文字の単語はそれらの小文字の等価物のすぐ隣に表示され、アクセント文字は対応するアクセント記号のない文字の近くにあります。

序数比較は、数値の値を厳密に比較し、最初の違いで停止します。これは大文字と小文字を完全に区別してソートします(そしてアクセント記号付きの文字はおそらくそれらと区別されます)ので、大文字の単語はそれらの小文字に相当するものの近くにはソートされません。

InvariantCultureは大文字も小文字よりも大きいと見なしますが、序数は大文字を小文字よりも小さいとみなします(ASCIIのホールドオーバーで、コンピューターに小文字が入る前に大文字が最初に割り当てられたため、大文字が優先されました)。後で追加された小文字よりも小さい値)。

たとえば、序数では"0" < "9" < "A" < "Ab" < "Z" < "a" < "aB" < "ab" < "z" < "Á" < "Áb" < "á" < "áb"

とInvariantCultureによって:"0" < "9" < "a" < "A" < "á" < "Á" < "ab" < "aB" < "Ab" < "áb" < "Áb" < "z" < "Z"

55
Rob Parker

問題は等価ですが、簡単に視覚的に参照できるように、ここでいくつかの文字列の順序並べ替えを使って、いくつかの特異点を説明します。

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

所見:

  • de-DEja-JP、およびen-USは同じ方法でソートします。
  • Invariantssßを上記の3つのカルチャとは異なる方法でのみソートします。
  • 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));
}
28

不変式は、言語学的に適切なタイプの比較です。
序数はバイナリ形式の比較です。 (もっと早く)
http://www.siao2.com/2004/12/29/344136.aspx を参照してください。

26
DanH

これは、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になります。

5
Dwedit

違いを示すために空想のUnicode文字の例を使う必要はありません。これは私が今日見つけた簡単な例ですが、これは驚くべきもので、ASCII文字だけで構成されています。

ASCII表によると、0(0x48)は通常の_(0x95)よりも小さいです。 InvariantCultureは反対のことを言います(下記のPowerShellコード)。

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