QA@IT
この質問・回答は、@ITの旧掲示板からインポートされたものです。

C#のString.IndexOfの仕様

文字列を操作する箇所でわからない動作をする箇所がありましたので、
もしわかる方がいらっしゃいましたら教えてください。

以下のような箇所でなぜか無限ループが発生してしまいます。



// 全角のハイフン
string h = "−";
// ハイフン二つ
string d = h + h;
StringBuilder sb = new StringBuilder("千代田区丸の内一−Oct−〇〇−");
while (sb.ToString().IndexOf(d) != -1) { // ここで見つかったと認識される
    sb.Replace(d, h);          // 実際にはないのでここで変換はされない
}

なぜかこれで無限ループが発生してしまいます。
ステップ実行で見ると、while文の条件内のIndexOfの戻り値が整数(−−が見つかった)
と認識されてしまってるようなのですが、
実際のデータには−−は含まれないのでReplaceでは何も起きなくて、
延々とループになってるようです。
−−に一致してると見なされるのは−と英字の並ぶ辺りなのですが、
−と英字が並んで−−に一致すると見なされるようなことはあるのでしょうか?
また、これは文字コードが何かからんでる問題なのでしょうか?

もしこれの原因に思い当たる方がいらっしゃいましたら、どうぞご教授をお願いいたします。

質問者:めー

回答

じゃんぬねっと です。

StringBuilder sb = new StringBuilder("千代田区丸の内一−Oct−〇〇−");

のラストの「〇−」が原因ですね。

_________________C# と VB.NET の入門サイト
じゃんぬねっと日誌

投稿者:じゃんぬねっと

編集 履歴 (0)

>じゃんぬねっと様
早速のご指摘ありがとうございます。

のラストの「〇−」が原因ですね。

とのことでしたので、"○"と"−"を見てみたところ、上位ビットが同じでした。
この辺りが原因なのでしょうか?

実はまだあまり理解してませんが、ループに回数制限をすればよいと気づいたので、
一応作業は先に進みました。
一息ついたらまた少しつっこんで見てみたいと思います。

ありがとうございました。

投稿者:めー

編集 履歴 (0)

少し実験してみました。

処理結果:
"AA".IndexOf("〇A") => 1
"AA".IndexOf("〇") => 0
"A〇A".IndexOf("AA") => 0
"〇A〇A".IndexOf("AA") => 1
"〇A〇A".IndexOf("〇A") => 0
"〇A〇A".LastIndexOf("〇A") => 3

'〇' という文字はまともに処理されていないような気がするのですが...

Google してみても見つかりませんでしたが、何か特殊な仕掛けが
あるのでしょうか?

投稿者:@echo

編集 履歴 (0)

こんにちは。

"〇"文字に問題があるみたいです。(U+3007)
-----------------------------------------------------
string target_text = "千代田区丸の内一−Oct−〇〇−";
target_text.IndexOf("〇"); → 0
target_text.IndexOf(String.Empty); → 0
-----------------------------------------------------
同じ結果返ってきます。

"−〇〇−"の"〇〇"が無視されて"−−"になり
ヒットし無限ループしてるように思います。

[ メッセージ編集済み 編集者: Milan 編集日時 2005-02-06 00:24 ]

投稿者:Milan

編集 履歴 (0)

String.IndexOf(String.Empty);
と同じ結果(ゼロ)が返ってきます。

なるほど! String.Empty と同じ扱いなんですね!?
...って納得していいんでしょうか...(^^;

  1. '〇' が String.Empty と同じ扱いなのは仕様なのでしょうか?
  2. "xxx".IndexOf(String.Empty) == 0 は仕様としては正しいのでしょうか?

ご存知の方いらっしゃいますか?

投稿者:@echo

編集 履歴 (0)
  1. '〇' が String.Empty と同じ扱いなのは仕様なのでしょうか?

  2. "xxx".IndexOf(String.Empty) == 0 は仕様としては正しいのでしょうか?

1は仕様ではないと思います。
 おそらく、IndexOfメソッドのバグなんではないでしょうか。
 LastIndexOf("〇")は正しく値が・・・(間違い、常に最後のIndexが返される)
 
2は仕様です。
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/cpref/html/frlrfsystemstringclassindexoftopic2.asp

[ メッセージ編集済み 編集者: Milan 編集日時 2005-02-06 00:19 ]

投稿者:Milan

編集 履歴 (0)

ありがとうございます。

 おそらく、IndexOfメソッドのバグなんではないでしょうか。

ですよね。こういうときソースが見れないのはつらいところです。

LastIndexOf("〇")は正しく値が帰ってきます。

"AAA".LastIndexOf"〇") = 2 なので誤っていますね。
IndexOf, LastIndexOf 共 NG です。

2は仕様です。

おおっ! 見落としていました。ありがとうございます。
[ メッセージ編集済み 編集者: suz21426 編集日時 2005-02-05 08:37 ]

投稿者:@echo

編集 履歴 (0)

こういうときソースが見れないのはつらいところです。
一応見るツールがありますよ(ILをC#に変換する)。全ては見れないかもしれませんが。
http://www.atmarkit.co.jp/fdotnet/tools/dotfuscator/dotfuscator_02.html
(StringはMscorlib.dll内ですね)
#見ましたが、私には複雑で、解析する気は起きませんでした・・

確かに「〇」の処理がおかしいメソッドがいくつかありますね。
中で使ってる共通の処理か何かがおかしいのでしょうかね。
[ メッセージ編集済み 編集者: べる 編集日時 2005-02-05 13:00 ]

投稿者:べる

編集 履歴 (0)

ありがとうございます。"Reflector" ですね。
リソース見るぐらいにしか使ってませんでした。(^^;;

で、追いかけてみましたが、CompareInfo の

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe int IndexOfString(...

の宣言で終わってしまいました...(中身なし)

投稿者:@echo

編集 履歴 (0)

この問題は、カルチャが関係しているのかなと思って調べていたのですが

これはって思ったのは
-------------------------------------------------------
String.Empty == "〇"; → False;
String.Compare( String.Empty, "〇" ); → 0(※)
String.CompareOrdinal( String.Empty, "〇" ); → -1
-------------------------------------------------------
だからIndexOfも・・・

StringクラスのIndexOfメソッドは
CompareInfoのIndexOfメソッドを内部で呼び出しています。
そのメソッドにCompareOptions(列挙体)を指定できるのですが、
そこにCompareOptions.Ordinal指定すると正しく処理されます。

---

CompareInfoクラスを使用するのも一つの手。
その他、代替としては
StringクラスのIndexOfAnyメソッドを使用しても
正しく処理できるかと思います。
-------------------------------------------------------
string maru = "〇";
string target_text = "千代田区丸の内一−Oct−〇〇−";
char[] maru_c = maru.ToCharArray();

target_text.IndexOfAny( maru_c );
-------------------------------------------------------

個人的には正規表現でやるのがスッキリする気がします。
余談ですが、"〇"文字(U+3007)は
"IDEOGRAPHIC NUMBER ZERO"って名前みたいです

---
最後に前に投稿したあいまいな情報は
紛らわしいので修正しておきます。
ご指摘ありがとうございました。

[ メッセージ編集済み 編集者: Milan 編集日時 2005-02-06 00:14 ]

投稿者:Milan

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。