• ベストアンサー

C#文字コードについて

お疲れ様です。 いつもお世話になっております。 ディレクトリを開いて、ファイルをstreamreaderで読み込み、 処理をしてstreamwriterで書き込み。 というファイルを作っていて、文字コードの問題にぶち当たりました。 http://dobon.net/vb/dotnet/string/detectcode.html にあるプログラムを実行すると、 ある条件の時に、UTFがsjisと誤認されることがあり、それをなんとか解決できないかと考えています。 ある条件とは、 あa テストPHP など、文字コードがUTF8で、全角と半角英字が隣り合う文字列が合った場合です。 全角と半角数字ではUTF8と正しく認識されるようです。 ただし、 aあ のように、半角英字全角の組み合わせでは誤認は起きません。 同じように、 あ a テスト php と、半角スペースが空いたり あ a と、全角スペースであけてもUTF8と認識されるようです。 普段PHPで、文字コードについて深く意識したことが無かったので、 なぜこういった誤認が起きるのかが検討つきません。 どういったことが原因だと考えられるのでしょうか? また、それを改善するのに何をすればよいでしょうか? よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • php504
  • ベストアンサー率42% (926/2160)
回答No.1

文字コードとして矛盾しない場合に優先順位が高い文字コードが選択されるためでしょう UTF-8の"あa"はShiftJISの"縺B"と同じバイトデータになります。(16進数でE3 81 82 61) 短い文字列では誤判定の可能性は避けられません。

nyalio
質問者

お礼

目から鱗です。 同じバイトデータが存在することを全く知りませんでした。 というか、バイナリで文字を扱うことがなかったので、 疑問に思う機会すらありませんでした。 この問題は非常に勉強になりました。 回答ありがとうございました。

すると、全ての回答が全文表示されます。

その他の回答 (2)

回答No.3

>なぜこういった誤認が起きるのかが検討つきません。 「検討」ではなく「見当」です。ここ最近よく見る誤字です。 >どういったことが原因だと考えられるのでしょうか? 「S-JISと解釈しても正しい文字コードとして解釈出来るし、UTF-8と解釈しても正しい文字コードとして解釈出来る場合」には、最終的に「どっちか判らない」と言う事になります。 で「どっちか判らない」では困るので「どっちに解釈しても最後までうまく解釈できちゃった時は、仕方が無いから、こっち」と解釈するしかありません。 例えば16進数で E3 82 9B E3 81 81 と言う6バイトのバイト列があったとします。 S-JISで解釈すると「繧帙=」と言う3文字です。 E3 82:繧 9B E3:帙 81 81:= UTF-8で解釈すると「゛ぁ」と言う2文字です。 E3 82 9B:゛ E3 81 81:ぁ あなたは、この「E3 82 9B E3 81 81」を見て「S-JISなのか、UTF-8なのか」が判断できますか?絶対に判断出来ませんよね。 仕方なく「S-JISだ」「UTF-8だ」と「当てずっぽう」で答えるしかありません。 そして、その「当てずっぽう」が外れた時「文字コードの誤認」が起きます。 例えば、googleでは、検索文字のコードは標準で「UTF-8」になっています。 なので http://www.google.co.jp/search?hl=ja&q=%E3%82%9B%E3%81%81&lr= を検索すると「゛ぁ」を含むページを検索しに行きます。 しかし、この検索文字の後ろに「S-JISでの全角スペース」を付けたし「S-JISとは解釈できるが、UTF-8とは解釈できない状態」にして http://www.google.co.jp/search?hl=ja&q=%E3%82%9B%E3%81%81%81%40&lr= を検索すると「繧帙= 」を含むページを検索しに行きます。 つまりこれは「S-JISで『繧帙=』を検索させると、UTF-8の『゛ぁ』と誤認識する」と言う事です。 これは「3バイト全角コード2文字と、2バイト全角コード3文字の、不幸な偶然の一致」です。 このような「不幸な偶然の一致」は「3バイト全角コード1文字と、2バイト全角1文字+1バイト半角カナ1文字」でも発生します。 16進数で E7 82 BA の3バイトのバイト列は UTF-8 E7 82 BA:為 S-JIS E7 82:轤 BA:コ(←これは半角カナの「コ」) となり、これも「どっちか判らない」って事になります。 >また、それを改善するのに何をすればよいでしょうか? 改善は事実上不可能です。 判断の精度を上げるなら 「先頭の1行だけで判断する」 のではなく 「ファイルの終わりまで、くまなく全部の行を調べ上げ、1ヶ所でもUTF-8と解釈できない部分があればUTF-8ではない、1ヶ所でもS-JISと解釈できない部分があればS-JISではない、のように、すべての文字セットについて全部調べ、ファイルの最後の最後までちゃんと解釈できた文字セットだけを採用」 とすれば、いくらか精度が上がります。 しかし、これは「処理が遅くなるだけで、何の解決にもならない。先頭の1行目だけ調べる方が、速度的に遥かにマシ」です。 仮に、ファイルの末尾まで全部スキャンするとしたら、以下のような問題が起きます。 ・読み込むテキストが3000万行あったら?全部読み込んで、全部の文字セットを試すだけで2時間かかるとしたら? ・全ての文字セットを試して、合格する文字セットが1つも無かったら?(前半がS-JIS、後半がUTF-8で書いてある、混在テキストの場合に起きる) ・ファイルの末尾まで調べて、結局、合格する文字セットが複数あったら?(短いテキストファイルだと、最後まで調べても、結局、S-JISかUTF-8か判らない、どっちとも解釈できる、と言う状態に) どうです?「結局、先頭の1行だけ見て判断するのと、たいして変わらない」と思いませんか? 根本的な解決は不可能ですので「誤認をさせない」というなら 「どっちとも解釈できるテキストファイルを食わせたら、ユーザーに『文字セットが判断できません。可能性のある文字セットは以下の通りです。文字セットを以下から選択して下さい』と言う選択をさせ、ユーザーに選択させた文字セットで変換する」 と言う方式にするしかありません。 それでも「ユーザーが選択を間違う」と「文字セットを誤解する」のは避けられません。 そういった訳で、一般のブラウザなども、文字セットの選択は「自動選択」の他に「強制的に、この文字セットと解釈する」と言う機能が必ず存在します。 悪の根源は「場合により、S-JISかUTF-8か、判断できなくなるような文字の割り当てをしてしまった、UTF-8の規格制定者」なので、どうしようもありません。 「こういう文字割り当てにしちゃった人」を恨むしかありません。

nyalio
質問者

お礼

ご丁寧にありがとうございます。 UTF-8の規格制定者をうらんでみます。

すると、全ての回答が全文表示されます。
回答No.2

とりあえず原因は E3 81 82 61 という文字コードの並びだけでは、UTF-8の「あa」と Shift_JISの「縺B」のどちらでも解釈できるからです。 半角空白等を含むとOKなのは、Shift_JISの2バイト目にくることがないので大丈夫なわけです。(\とかはダメな時もある。) 逆に前に半角英字のaとかは、Shift_JISの1バイト目にくることがないので同様です。 文字コードの判定はある程度長い文字列を指定しないといけません。 BOMをつけれるならつけるとか、どちらか判断がつかないときは、ユーザに選択させるとか。 以下実験したコード byte[] b = System.Text.Encoding.GetEncoding("UTF-8").GetBytes("あa"); StringBuilder sb = new StringBuilder(); for (int i = 0; i < b.Length; ++i) { sb.AppendFormat("{0:X}", b[i]); } Console.Write("{0}\n", sb.ToString()); string s = System.Text.Encoding.GetEncoding("Shift_JIS").GetString(b); Console.Write("{0}\n", s);

nyalio
質問者

お礼

実際全角+半角を含むwebページの文字コードなどをサンプルに落として来た感じではうまくいっていました。 やはり短い文章だけでは判断できないのですね。 すごく勉強になりました。

すると、全ての回答が全文表示されます。

関連するQ&A