VB.net 2005 を使用しています。
以下の文字列検索で想像した結果になりません。
キャラクタセット関連の問題でしょうか?
Dim s2 As String
s2 = "abc゛xyz" 'cとxの間に全角文字が1つあります Hex dump 61 62 63 81 4A 78 79 7A
MsgBox(CStr(s2.IndexOf("a", 0))) '0 になります
MsgBox(CStr(s2.IndexOf("b", 0))) '1 になります
MsgBox(CStr(s2.IndexOf("c", 0))) '-1 になります
MsgBox(CStr(s2.IndexOf("゛", 0))) '3 になります
MsgBox(CStr(s2.IndexOf("x", 0))) '4 になります
MsgBox(CStr(s2.IndexOf("y", 0))) '5 になります
MsgBox(CStr(s2.IndexOf("z", 0))) '6 になります
MsgBox(CStr(s2.IndexOf("bc", 0))) '-1 になります
MsgBox(CStr(s2.IndexOf("abc", 0))) '-1 になります
上記、"c" が絡むと -1 が返されます。
ご存知の方、ご教授頂けませんでしょうか。
InStrではだめなんですか?
もしかして2005にはないんでしょうか・・・
検索文字列が1文字なら
MsgBox(CStr(s2.IndexOf("c"c, 0)))
で3って値になりますね。
> InStrではだめなんですか?
InStrだと正しい値が取れますね。
謎。。。
皆様、ありがとうございます。
InStr で上手く行きますね。
全角文字が他の文字なら IndexOf でも上手くゆくので、UniCode関連(詳しくありません)など Encodingのお作法を私が知らないからかと思いました。
InStr にて対症します。
.NET だけでなく、データベースの Collate などにも言える話ですが、
Unicode の世界では、濁点・半濁点のように、直前の文字と組み合わせて
使われる文字は、一見、特殊な動作をすることがあります。
たとえば、「℃」という 1 文字は、U+00B0, U+0043 という
2 文字に分解したマッピング方法もあったりします。
http://homepage1.nifty.com/nomenclator/unicode/normalization.htm
そのせいかどうかはわかりませんが、たとえばこんな事も起きうるわけで。
s2 = "今日は12℃あります。"
MsgBox(CStr(s2.IndexOf("℃", 0))) ' 5 (通常)
MsgBox(CStr(s2.IndexOf("C", 0))) '-1
MsgBox(CStr(s2.IndexOf("C゜", 0))) ' 5 (合成文字扱い?)
MsgBox(CStr(s2.IndexOf("C゛", 0))) '-1
MsgBox(CStr(s2.IndexOf("C゛゛", 0))) ' 5 (なぜ?)
# このあたりは、Microsoft Access などでも、昔から同様の仕様であり、
# x = "◎゛".CompareTo("●")
# y = "◎゜".CompareTo("●゛")
# などに至っては、もう何が何やら。(^_^;)
# http://www.asahi-net.or.jp/~ez3k-msym/comp/acccoll.htm
> InStr で上手く行きますね。
VBの場合、Option Compare 設定の影響を受けるので、これはこれで、
String.IndexOf とは異なる文字列処理方法が組み込まれているそうです。
ちなみに、こういう話もあったり。
Dim S As String = "フ゛イ"
'「ブイ」→「V」
Dim A As String = S.Replace("ブイ", "V") 'フ゛イ
Dim B As String = Replace(S, "ブイ", "V", , , CompareMethod.Binary) 'フ゛イ
Dim C As String = Replace(S, "ブイ", "V", , , CompareMethod.Text) 'Vイ
'「フ」→「@」
Dim D As String = S.Replace("フ", "@") '@イ
Dim E As String = Replace(S, "フ", "@", , , CompareMethod.Binary) '@イ
Dim F As String = Replace(S, "フ", "@", , , CompareMethod.Text) '@イ
'「フ゛」→「*」
Dim G As String = S.Replace("フ゛", "*") '*イ
Dim H As String = Replace(S, "フ゛", "*", , , CompareMethod.Binary) '*イ
Dim I As String = Replace(S, "フ゛", "*", , , CompareMethod.Text) '*イ
'「フ゛」→「*」
Dim J As String = S.Replace("゛", "%") 'フ%イ
Dim K As String = Replace(S, "゛", "%", , , CompareMethod.Binary) 'フ%イ
Dim L As String = Replace(S, "゛", "%", , , CompareMethod.Text) 'フ%イ
ありがとうございます。
基本的な文字列操作のような案件でしたが、ご説明頂いたような深淵(コードに対する考え方など)がありそうだと感じたので質問させていただきました。
まさに「℃」という 1 文字を分割するかどうかも考え方によると思います。
簡単そうですが、ここまで(これ以外にも)理解しないと正しいプログラムが組めないと言うことを痛感します。
なるほど、複雑なものですね。(メモ、メモ..)
ところで、
>>MsgBox(CStr(s2.IndexOf("c", 0)))
は
MsgBox(CStr(s2.IndexOf(CType("c", Char), 0)))
MsgBox(CStr(s2.IndexOf("c"c, 0)))
これの方が正しい気がするのだがなんとなく(気のせいかな)
String.IndexOf (Char, Int32) を呼び出すか、
String.IndexOf (String, Int32)を呼び出すかの違いなんですが、
で、こうすると、Char単位では正確に取得出来ますな。
でもやっぱり、"bc","abc"の文字列はダメなんで、
この場合、何の解決にもなりませんが。
ツイート | ![]() |