Web上からHTMLファイルを読み取るときの文字コードを自動で指定したい への返答
投稿で使用できる特殊コードの説明。(別タブで開きます。)
以下の返答は逆順(新しい順)に並んでいます。
投稿者 VANITAS  (社会人)
投稿日時
2011/7/6 22:49:59
Dim internet As New Net.WebClient
Dim st As System.IO.Stream = internet.OpenRead("http://hogehoge.com/foobar.html")
Dim sr As New System.IO.StreamReader(st, Encoding.GetEncoding("EUC-JP"))
Dim source As String = sr.ReadToEnd()
st.Close()
internet.Dispose()
tbSource.Text = source
このようなコードを書いたとします。なおURLを仮のものです。
ここでは文字コードが「EUC-JP」となっていますが、実際不確定のURLからhtmlファイルを読み込むときは文字コードがわかりません。
どのようにすれば正しい文字コードを読み込む前に判別できるのでしょうか?
> なおURLを仮のものです。
URL やメールアドレスを例示する場合には、hogehoge.com を使うのではなく
example.com や example.net 等を使うようにしましょう。
これは例示するために予約されたドメイン名です。(RFC 2606 の 3 章を参照)
http://allabout.co.jp/gm/gc/23887/
http://neta.ywcafe.net/000501.html
> どのようにすれば正しい文字コードを読み込む前に判別できるのでしょうか?
Web ページにおける文字コードの取得には
(1) HTTPレスポンスヘッダー中の
「Content-Type:text/html; charset=ISO-2022-JP」。
(2) HTML の <head> タグ配下にある
「<meta http-equiv="Content-Type" content="text/html; charset=euc-jp">」。
(3) 文書の先頭にある XML 宣言
「<?xml version="1.0" encoding="Shift_JIS"?>」。
(4) 文書の先頭数バイトが示す バイトオーダーマーク(BOM)。
「EF BB BF」のバイナリだった場合は UTF-8、
「FF FE」のバイナリだった場合は、(リトルエンディアンの) UTF-16、
「FE FF」のバイナリだった場合は、(ビッグエンディアンの) UTF-16、
を使う事ができるとされています。
しかし(1)は、charset が未指定なサーバーも少なくないため、当てにはできません。
そもそも、ここの charset が実際のファイルの内容と合致していないケースもままあります。
HTML ファイルの内容と違って、HTTP ヘッダーの内容は確認しずらいということもあり、
サイト管理者が十分な知識を持たない場合、誤った設定であることに気づきにくいためです。
次に(2)。これは文書自体に誤記されていなければある程度信頼できますが、
これが記載されていない HTML 文書も存在するため、確実な方法ではありません。
(3) はかなり信頼できる情報ですが、そもそも HTML 文書では XML 宣言がありません。
これが含まれるのは、XML 文書や XHTML 文書用の記述に限られます。しかも、たとえ
XML 文書であったとしても、Unicode (UTF-8 とか UTF-16 とか)で記述された文書では、
XML 宣言を省略できることになっています。
(4) も信頼性の高い情報です。これが検出された場合は、確実に文字コードを判定できます。
ただし、BOM そのものは必須ではありませんので、これらが検出されない文書の方が多いです。
しかも BOM が使われるのは UTF-7、UTF-8、UTF-16、UTF-32 に限られており、
Shift_JIS や EUC-JP 等では利用されません。
…ということで、残念ながら確実な方法というものは実はありません。
ですから、まずは文字列として読み取るのではなく、バイナリとして取得しておき、
そこから複数のエンコーディング候補を一つ一つ試してみて、その中で
デコードエラーが起きた物を除外する方向で実装してみては如何でしょう。
エンコードエラーやデコードエラーを検知する場合には、
System.Text.Encoding.GetEncoding メソッドの第2/第3引数に
System.Text.EncoderExceptionFallback クラス
System.Text.DecoderExceptionFallback クラス
を指定すれば OK です。その文字コードでは扱えないデータが
含まれていた場合には例外が発生しますので、Try~Catch で検知します。
(例外の内容から、どのデータが例外の原因になったのかを調べることもできます)