42

基になる順序演算子が推移的反対称であることは、比較並べ替えが機能するための要件です。

.NET では、一部の文字列には当てはまりません。

static void CompareBug()
{
  string x = "\u002D\u30A2";  // or just "-ア" if charset allows
  string y = "\u3042";        // or just "あ" if charset allows

  Console.WriteLine(x.CompareTo(y));  // positive one
  Console.WriteLine(y.CompareTo(x));  // positive one
  Console.WriteLine(StringComparer.InvariantCulture.Compare(x, y));  // positive one
  Console.WriteLine(StringComparer.InvariantCulture.Compare(y, x));  // positive one

  var ja = StringComparer.Create(new CultureInfo("ja-JP", false), false);
  Console.WriteLine(ja.Compare(x, y));  // positive one
  Console.WriteLine(ja.Compare(y, x));  // positive one
}

は厳密には よりも大きく、厳密xには よりも大きいことがわかります。yyx

x.CompareTo(x)などはすべてゼロ ( ) を与えるため0、これが注文ではないことは明らかです。当然のことながら、 や のような文字列を含む配列またはリストを作成すると、予測できない結果が得られSortます。私はこれをテストしていませんが、キーにやのような文字列が使用されている場合、ソートされた順序でそれ自体を維持したり、アイテムを見つけたりするのに問題があると確信しています。xySortedDictionary<string, WhatEver>xy

このバグは有名ですか?どのバージョンのフレームワークが影響を受けますか (.NET 4.0 で試しています)?

編集:

次の例では、どちらの場合でも符号が負になります。

x = "\u4E00\u30A0";         // equiv: "一゠"
y = "\u4E00\u002D\u0041";   // equiv: "一-A"
4

2 に答える 2

17

問題で正しい並べ替えが非常に重要な場合は、カルチャに依存する代わりに序数に基づく文字列比較を使用してください。これだけが、必要な推移的および反対称の比較を保証します。

MSDN の説明:

メソッド呼び出しで StringComparison.Ordinal または StringComparison.OrdinalIgnoreCase 値を指定することは、自然言語の機能が無視される非言語比較を意味します。これらの StringComparison 値を使用して呼び出されるメソッドは、カルチャによってパラメーター化された大文字と小文字または等価テーブルではなく、単純なバイト比較に基づいて文字列操作を決定します。ほとんどの場合、このアプローチは、コードの高速化と信頼性の向上を図りながら、意図した文字列の解釈に最も適しています。

そして、期待どおりに動作します:

    Console.WriteLine(String.Compare(x, y, StringComparison.Ordinal));  // -12309
    Console.WriteLine(String.Compare(y, x, StringComparison.Ordinal));  // 12309

はい、カルチャに依存した比較で一貫性のない結果が得られる理由を説明していません。まあ、奇妙な文化 — 奇妙な結果です。

于 2012-11-06T17:11:52.767 に答える
1

このSOの投稿に出くわしました.SortedListに挿入された(文字列)キーを取得する際に問題が発生した理由を理解しようとしているときに、原因が.Net 40以降の比較子(a1 < a2 および a2 < a3、ただし a1 > a3)。

何が起こっていたのかを理解するための私の苦労はここにあります: c# SortedList<string, TValue>.ContainsKey for successfully added key returns false .

私のSO質問の「UPDATE 3」セクションをご覧ください。この問題は 2012 年 12 月に Microsoft に報告され、2013 年 1 月末までに「修正されない」としてクローズされたようです。さらに、使用できる回避策を示します。

この推奨される回避策の実装を作成し、発生した問題が修正されたことを確認しました。また、これにより、ご報告いただいた問題が解決されることも確認いたしました。

public static void SO_13254153_Question()
{
    string x = "\u002D\u30A2";  // or just "-ア" if charset allows
    string y = "\u3042";        // or just "あ" if charset allows        

    var invariantComparer = new WorkAroundStringComparer();
    var japaneseComparer = new WorkAroundStringComparer(new System.Globalization.CultureInfo("ja-JP", false));
    Console.WriteLine(x.CompareTo(y));  // positive one
    Console.WriteLine(y.CompareTo(x));  // positive one
    Console.WriteLine(invariantComparer.Compare(x, y));  // negative one
    Console.WriteLine(invariantComparer.Compare(y, x));  // positive one
    Console.WriteLine(japaneseComparer.Compare(x, y));  // negative one
    Console.WriteLine(japaneseComparer.Compare(y, x));  // positive one
}

残りの問題は、この回避策が非常に遅く、文字列の大規模なコレクションで使用するのはほとんど実用的ではないということです。したがって、マイクロソフトがこの問題を解決することを再検討するか、誰かがより良い回避策を知っていることを願っています.

于 2013-07-12T23:02:08.093 に答える