55

最近、すべてのプロジェクトを .NET 3.5 から .NET 4 にアップグレードしましたstring.IndexOf()

私のコードは明らかに少し違うことをしていますが、問題を調査する過程でIndexOf()、文字列自体を呼び出すと、0 ではなく 1 が返されることがわかりました。つまり、次のようになります。

string text = "\xAD\x2D";          // problem happens with "­-dely N.China", too;
int index = text.IndexOf(text);    // see update note below.

インデックスを 0 ではなく 1 にしてくれました。この問題について注意すべき点がいくつかあります。

  • 問題はこれらのハイフンに関連しているようです (最初の文字は Unicode ソフト ハイフンで、2 番目の文字は通常のハイフンです)。

  • 再確認しましたが、これは .NET 3.5 では発生しませんが、.NET 4 では発生します。

  • を序数比較を行うように変更するIndexOf()と問題が修正されるため、何らかの理由で最初の文字がデフォルトで無視されますIndexOf

なぜこれが起こるのか誰か知っていますか?

編集

申し訳ありませんが、元の投稿に少し手を加えて、そこに隠しダッシュを2回入れました。文字列を更新しました。正しいエディターに貼り付ければ、2 ではなく 1 のインデックスが返されます。

アップデート:

元の問題文字列を、実際のすべての文字がはっきりと見える文字列に変更しました (エスケープを使用)。これにより、質問が少し単純化されます。

4

3 に答える 3

31

文字列には、ソフト ハイフン(Unicode コード ポイント 173) とハイフン(Unicode コード ポイント 45)の 2 つの文字が含まれます。

Wiki : Unicode 規格に従って、行がその時点で改行されていない場合、ソフト ハイフンは表示されません。

.NET 4 で使用"\xAD\x2D".IndexOf("\xAD\x2D")すると、ソフト ハイフンを探していることを無視しているように見え、開始インデックス 1 (のインデックス\x2D) が返されます。.NET 3.5 では、これは 0 を返します。

このコードを実行すると、さらに楽しくなります (つまり、ソフト ハイフンのみを探す場合):

string text = "\xAD\x2D";
string shy = "\xAD";
int i1 = text.IndexOf(shy);

その後i1、使用されている .NET バージョンに関係なく 0 になります。の結果text.IndexOf(text);は確かにさまざまで、一見するとバグのように見えます。

フレームワークを遡って追跡できる限り、古い .NET バージョンはInternalCall to を使用しますIndexOfString()(どの API 呼び出しに行くのかわかりません)。一方、.NET 4からはQCall toInternalFindNLSStringEx()が作成され、次にFindNLSStringEx().

問題(これが意図した動作であるかどうかは本当にわかりません)は、実際に呼び出すときに発生しますFindNLSStringEx

LPCWSTR lpStringSource = L"\xAD\x2D";
LPCWSTR lpStringValue = L"\xAD";

int length;

int i = FindNLSStringEx(
    LOCALE_NAME_SYSTEM_DEFAULT,
    FIND_FROMSTART,
    lpStringSource,
    -1,
    lpStringValue,
    -1,
    &length,
    NULL,
    NULL,
    1);

Console::WriteLine(i);

i = FindNLSStringEx(
    LOCALE_NAME_SYSTEM_DEFAULT,
    FIND_FROMSTART,
    lpStringSource,
    -1,
    lpStringSource,
    -1,
    &length,
    NULL,
    NULL,
    1);

Console::WriteLine(i);

Console::ReadLine();

0 を出力してから 1 をlength出力します。検出された文字列の長さを示す出力パラメーターである は、最初の呼び出しの後に 0 になり、2 番目の呼び出しの後に 1 になることに注意してください。ソフトハイフンは長さ 0 としてカウントされます。

text.IndexOf(text, StringComparison.OrdinalIgnoreCase);あなたが指摘したように、回避策は を使用することです。これにより、QCall が作成さInternalCompareStringOrdinalIgnoreCase()れ、次に が呼び出さFindStringOrdinal()れ、どちらの場合も 0 が返されます。

于 2012-07-13T09:20:05.240 に答える
20

これは.NET4のバグのようで、新しい変更は.NET4 Beta1で.NET 2.0/3.0/3.5と同じ以前のバージョンに戻りました。

.NET 4.0 CTP の BCL の新機能(MSDN ブログ) :

.NET 4 での文字列セキュリティの変更

System.String の既定の部分一致オーバーロード (StartsWith、EndsWith、IndexOf、および LastIndexOf) は、既定でカルチャに依存しない (序数) に変更されました。

String.IndexOfこの変更は、デフォルトで序数 (バイトごと) の比較を実行するようにメソッドを変更することで、メソッドの動作に影響を与えましCultureInfo.InvariantCultureCultureInfo.CurrentCulture

.NET 4 ベータ 1 の更新

.NET 4 と以前のリリースとの高い互換性を維持するために、この変更を元に戻すことにしました。String のデフォルトの部分一致オーバーロードと、String および Char の ToUpper メソッドと ToLower メソッドの動作は、.NET 2.0/3.0/3.5 と同じように動作するようになりました。元の動作に戻す変更は、.NET 4 Beta 1 にあります。


これを修正するSystem.StringComparisonには、文字列比較メソッドを列挙型をパラメーターとして受け入れるオーバーロードに変更し、Ordinalまたはを指定しますOrdinalIgnoreCase

// string contains 'unicode dash' \x2D
string text = "\xAD\x2D"; 

// woks in .NET 2.0/3.0/3.5 and .NET 4 Beta 1 and later
// but seems be buggy in .NET 4 because of 'culture-sensitive' comparison        
int index = text.IndexOf(text); 

// fixed version
index = text.IndexOf(text, StringComparison.Ordinal); 
于 2012-07-13T09:21:09.617 に答える
0

ドキュメントから(私の強調):

このメソッドは、現在のカルチャを使用して単語 (大文字と小文字を区別し、カルチャを区別する) 検索を実行します。

すなわち。いくつかの異なるコードポイントは等しいものとして扱われます。

文化的な依存関係を回避するために、StringComparison値とパスを受け取るオーバーロードを使用するとどうなりますか?StringComparison.Ordinal

于 2012-07-13T09:44:33.750 に答える