ReadJEncを使って文字エンコーディングを推定するには?[C#、VB]:.NET TIPS
Webページの取得を例に、MITライセンス準拠のドネーションウェア「ReadJEnc」を使用して、文字エンコーディングを推定する方法を解説する。
対象:.NET 2.0以降(Webページを取得する部分は.NET 4.5以降)
任意のWebページの内容を取得するには、現状では文字コードの推定が必要になる。「.NET TIPS:HttpClientクラスでシフトJISのWebページを取得するには?[C#、VB]」では、W3Cの推奨方式に準じて実装してみた。それでも文字化けしてしまうWebサイトでは、どうしたらよいだろうか? それには、Webページの内容を単にbyteの並びとして取得し、そのbyte列のパターンから文字エンコーディングを推定することになるだろう。本稿では、文字エンコーディングを推定して文字列に変換してくれるオープンソースのライブラリ「ReadJEnc」の使い方を解説する。
文字エンコーディングを推定するライブラリ
バイト列のパターンから文字エンコーディングを確実に判定できるアルゴリズムは存在しない。そこで、文字コードの「自動判別」とよくいわれるが、本稿では「推定」という言葉を使うことにする。そのような文字エンコーディングを推定するライブラリはいくつも公開されている。例えば次のようなものがある。
- IMultiLanguage2::DetectInputCodepage: Internet Explorerの判定ロジック(COMインターフェース)
- Universalchardet: Mozillaで開発された判定ロジック(C++ソースコード)
これらはInternet ExplorerやFirefoxで使われているもので推定精度は高そうではあるが、C#/VBから利用するにはハードルも高い。手軽に.NET Frameworkから直接参照できる形式のライブラリはないものか。その一つに、hnx8氏によって2014年に公開された「ReadJEnc」がある。ReadJEncはMITライセンス準拠のドネーションウェアとなっている。本稿では、Webページをbyte配列として取得し、それをReadJEncで文字列に変換する方法を紹介する。
ReadJEncを利用する準備
ダウンロードページから入手したzipファイルの中身を適当なフォルダーに展開しておく。その中の「Hnx8.ReadJEnc.dll」ファイルをプロジェクトの参照設定に追加すれば、準備は完了だ(次の画像)。
プロジェクトにReadJEncへの参照を追加(Visual Studio 2012)
これはVisual Studio Express 2012 for Windows Desktopの例である。
ソリューションエクスプローラーで参照を追加したいプロジェクトのプロジェクト名(または、そのプロジェクト内のファイル/フォルダー)を選択しておき、メニューバーから[プロジェクト]−[参照の追加] を選ぶ((1))。
すると[参照マネージャー]ダイアログが出てくるので、左側で[ブラウズ](赤枠内)を選び(Visual Studioのバージョンによっては[ブラウズ]ではなく[参照])、右下の[参照]ボタン(赤枠内)をクリックする((2))。なお、Visual Studioの以前のバージョンでは、ダイアログのタイトルは[参照の追加]となっており、[参照]タブを選んでファイルを選択する(その場合、次の(3)はない)。
出てきたファイル選択ダイアログで「Hnx8.ReadJEnc.dll」ファイル(赤枠内)を選び、[追加]ボタン(赤枠内)をクリックする((3))。
[参照マネージャー]ダイアログに戻るので、右下の[OK]ボタンをクリックして完了だ((4))。
なお、「Hnx8.ReadJEnc.dll」ファイルと同じフォルダーに「Hnx8.ReadJEnc.xml」ファイルがあれば(zipファイルをそのまま展開したら存在するはずだ)、Visual StudioのIntelliSenseが機能する(次の画像)。
ReadJEncへの参照を追加した後はIntelliSenseも機能する(Visual Studio 2012)
これはVisual Studio Express 2012 for Windows Desktopの例である。
参照設定で追加した「Hnx8.ReadJEnc.dll」ファイルと同じフォルダーに「Hnx8.ReadJEnc.xml」ファイルがあれば、このようにIntelliSenseが機能する。
Webページをbyte配列として取得するには?
HttpClientクラス(System.Net.Http名前空間)を利用するなら、GetByteArrayAsyncメソッドを使えばよい。
「.NET TIPS:HttpClientクラスでWebページを取得するには?[C#、VB]」で作成したGetWebPageAsyncメソッドをベースにして書き換えると、次のコードのようになる。
static async Task<byte[]> GetWebPageAsync(Uri uri)
// メソッドが返す型はTask<string>からTask<byte[]>に変更する
{
// HttpClientオブジェクトを作って使用する
using (var client = new HttpClient())
{
……省略……
try
{
//// Webページの内容を文字列として取得する
//return await client.GetStringAsync(uri);
// Webページの内容をbyte配列として取得する
return await client.GetByteArrayAsync(uri);
}
catch (HttpRequestException e)
{
……省略……
}
return null;
}
}
Async Function GetWebPageAsync(uri As Uri) As Task(Of Byte())
' メソッドが返す型はTask Task(Of String)からTask(Of Byte())に変更する
' HttpClientオブジェクトを作って使用する
Using client = New HttpClient()
……省略……
Try
'' Webページの内容を文字列として取得する
'Return Await client. GetStringAsync (uri)
' Webページの内容をbyte配列として取得する
Return Await client.GetByteArrayAsync(uri)
Catch e As HttpRequestException
……省略……
End Try
Return Nothing
End Using
End Function
「.NET TIPS:HttpClientクラスでWebページを取得するには?[C#、VB]」で作成したGetWebPageAsyncメソッドをベースにして、メソッドが返す型を変更し、HttpClientクラスのGetStringAsyncメソッドを呼び出している部分をGetByteArrayAsyncメソッドに書き換えた。省略した部分は同じである。
なお、HttpClientクラスは.NET 4.5で新設されたものだ。それ以前のバージョンではWebClientクラス(System.Net名前空間)のDownloadDataメソッドを使うなどしてほしい(「.NET TIPS:WebClientクラスでWebページを取得するには?」参照)。
取得したbyte配列をReadJEncで文字列に変換するには?
上のメソッドで取得できたbyte配列を、ReadJEncクラスのインスタンスのGetEncodingメソッドに渡せばよい。ReadJEncクラスが文字エンコーディングを推定して文字列に変換した結果が得られる。また、このメソッドの返値は、推定できた文字エンコーディングとなっている(次のコード)。
……省略……
static void Main(string[] args)
{
……省略……
// 取得したいWebページのURI
Uri webUri = new Uri(……省略……);
// GetWebPageAsyncメソッドを呼び出す
//Task<string> webTask = GetWebPageAsync(webUri);
Task<byte[]> webTask = GetWebPageAsync(webUri);
webTask.Wait(); // Mainメソッドではawaitできないので、処理が完了するまで待機する
//string result = webTask.Result; // 結果を取得
byte[] byteData = webTask.Result; // 結果を取得
string result = null; // デコード結果が格納されるstring変数
if (byteData != null)
{
// byte配列として取得できたので、ReadJEncのJPインスタンスを使ってデコードする
Hnx8.ReadJEnc.CharCode charCode
= Hnx8.ReadJEnc.ReadJEnc.JP.GetEncoding(byteData, byteData.Length, out result);
Console.WriteLine("文字エンコーディング推定結果: {0}", charCode.ToString());
}
……省略(経過時間と得られた文字列を表示)……
}
Sub Main()
……省略……
' 取得したいWebページのURI
Dim webUri As Uri = New Uri(……省略……)
' GetWebPageAsyncメソッドを呼び出す
'Dim webTask As Task(Of String) = GetWebPageAsync(webUri)
Dim webTask As Task(Of Byte()) = GetWebPageAsync(webUri)
webTask.Wait() ' Mainメソッドではawaitできないので、処理が完了するまで待機する
'Dim result As String = webTask.Result ' 結果を取得
Dim byteData As Byte() = webTask.Result ' 結果を取得
Dim result As String = Nothing ' デコード結果が格納されるstring変数
If (byteData IsNot Nothing) Then
' byte配列として取得できたので、ReadJEncのJPインスタンスを使ってデコードする
Dim charCode As Hnx8.ReadJEnc.CharCode _
= Hnx8.ReadJEnc.ReadJEnc.JP.GetEncoding(byteData, byteData.Length, result)
Console.WriteLine("文字エンコーディング推定結果: {0}", charCode.ToString())
End If
……省略(経過時間と得られた文字列を表示)……
End Sub
「.NET TIPS:HttpClientクラスでWebページを取得するには?[C#、VB]」で作成したMainメソッドに対して、太字の部分を変更/追加した。
ReadJEncクラスのコンストラクターはアクセスできないようになっている。インスタンスを得るには、ReadJEncクラスの静的フィールド「JP」(日本語用)/「CN」(簡体字中国語用)などを使う。
ReadJEncクラスのGetEncodingメソッドを呼び出した後は、ローカル変数「result」にエンコードされた文字列が入っている。また、GetEncodingメソッドの返値を格納したローカル変数「charCode」には、ReadJEncクラスが推定した文字エンコーディングが入っている。なお、文字エンコーディングが推定できなかったときには、ローカル変数「result」/「charCode」は共にnull(C#)/Nothing(VB)になる(このサンプルコードでは省略しているが、エラー処理を行うべきである)。
このサンプルコードで、WebページのURL(ローカル変数「webUri」を作るときに与える文字列、コード内では省略している)を変えて試した結果を以下に示す。
日本語のサイト、あるいは他言語であってもUTF-8のサイトでは、きちんとデコードできた(次の画像)。
日本語のサイト、UTF-8の他言語のサイトでは正しく推定できる
上から順に、シフトJIS/JIS/EUC-JP/UTF-8(英語)/UTF-8(中国簡体字)/UTF-8(ロシア語)。
なお、中国語の一部が文字化けしているように見えるが、これはエンコードに失敗しているわけではなく、実行環境のフォント(=MSゴシック)に該当するグリフが存在しないためである。
ReadJEncクラスの「JP」インスタンスでは、UTF-8ではない他言語のサイトは正しく推定できない(次の画像)。これは、言語ごとにインスタンスが分かれていることから想像できる結果である(画像にも載せたが、「CN」インスタンスを使えば中国語簡体字のエンコーディングを推定できる)。
「JP」インスタンスでは、UTF-8ではない他言語のサイトは正しく推定できない
上は「JP」インスタンスで、下は「CN」インスタンスでデコードした。どちらも、Webページの文字エンコーディングはGB2312(中国簡体字)である。
日本語用の「JP」インスタンスは、他言語のサイトではエンコーディングを正しく推定できない。
中国語簡体字用の「CN」インスタンスを使えば、簡体字のエンコーディングを推定できる(その代わりに日本語のサイトが推定できない)。ReadJEncクラスにはその他に、中国語繁体字用「TW」インスタンス/韓国語用「KR」インスタンス/欧文用「ANSI」インスタンスも用意されている。
なお、下の画像で中国語の一部が文字化けしているように見えるが、これはエンコードに失敗しているわけではなく、実行環境のフォント(=MSゴシック)に該当するグリフが存在しないためである。
上述した注意点があることをわきまえて使えば、ReadJEncクラスはとても便利なライブラリである。その他、ReadJEncクラスの詳細は作者のブログ記事をご覧いただきたい。また、ダウンロードできるパッケージにはC#のソースコードも同梱されている。
利用可能バージョン:.NET Framework 2.0以降
カテゴリ:クラスライブラリ 処理対象:文字列 処理対象:ネットワーク
使用ライブラリ:HttpClientクラス(System.Net.Http名前空間)
使用ライブラリ:ReadJEncクラス(Hnx8.ReadJEnc名前空間)
関連TIPS:HttpClientクラスでWebページを取得するには?[C#、VB]
関連TIPS:HttpClientクラスでシフトJISのWebページを取得するには?[C#、VB]
関連TIPS:WebClientクラスでWebページを取得するには?
Copyright© Digital Advantage Corp. All Rights Reserved.