文字コードを判別する.NET Frameworkでは文字コードを判別する方法が用意されていませんので、外部DLL、OCX等を使うか、自分でコードを書くかということになります。 Jcode.pmを参考にした方法次に示すコードは、私がJcode.pmのgetcodeメソッドを参考にして書かせていただいた(移植したつもり)メソッドです。バイナリ配列で渡されたデータが、JIS、Shift-JIS、EUC、UTF-8(もしくはASCII)のいずれであるかを判別し、結果をEncodingオブジェクトで返します。 [VB.NET] ''' <summary> ''' 文字コードを判別する ''' </summary> ''' <remarks> ''' Jcode.pmのgetcodeメソッドを移植したものです。 ''' Jcode.pm(http://openlab.ring.gr.jp/Jcode/index-j.html) ''' Jcode.pmのCopyright : Copyright 1999 Dan Kogai. ''' </remarks> ''' <param name="byts">文字コードを調べるデータ</param> ''' <returns>適当と思われるEncodingオブジェクト。 ''' 判断できなかった時はnull。</returns> Public Shared Function GetCode(ByVal byts() As Byte) As System.Text.Encoding Const bESC As Byte = &H1B Const bAT As Byte = &H40 Const bDollar As Byte = &H24 Const bAnd As Byte = &H26 Const bOP As Byte = &H28 '( Const bB As Byte = &H42 Const bD As Byte = &H44 Const bJ As Byte = &H4A Const bI As Byte = &H49 Dim len As Integer = byts.Length Dim [binary] As Integer = 0 Dim ucs2 As Integer = 0 Dim sjis As Integer = 0 Dim euc As Integer = 0 Dim utf8 As Integer = 0 Dim b1, b2 As Byte Dim i As Integer For i = 0 To len - 1 If byts(i) <= &H6 OrElse byts(i) = &H7F OrElse byts(i) = &HFF Then ''binary' [binary] += 1 If len - 1 > i AndAlso byts(i) = &H0 AndAlso _ i > 0 AndAlso byts(i - 1) <= &H7F Then 'smells like raw unicode ucs2 += 1 End If End If Next i If [binary] > 0 Then If ucs2 > 0 Then 'JIS 'ucs2(Unicode) Return System.Text.Encoding.Unicode 'binary Else Return Nothing End If End If For i = 0 To len - 2 b1 = byts(i) b2 = byts(i + 1) If b1 = bESC Then If b2 >= &H80 Then 'not Japanese 'ASCII Return System.Text.Encoding.ASCII Else If len - 2 > i AndAlso _ b2 = bDollar AndAlso byts(i + 2) = bAT Then 'JIS_0208 1978 'JIS Return System.Text.Encoding.GetEncoding(50220) ElseIf len - 2 > i AndAlso _ b2 = bDollar AndAlso byts(i + 2) = bB Then 'JIS_0208 1983 'JIS Return System.Text.Encoding.GetEncoding(50220) ElseIf len - 5 > i AndAlso _ b2 = bAnd AndAlso byts(i + 2) = bAT AndAlso _ byts(i + 3) = bESC AndAlso byts(i + 4) = bDollar AndAlso _ byts((i + 5)) = bB Then 'JIS_0208 1990 'JIS Return System.Text.Encoding.GetEncoding(50220) ElseIf len - 3 > i AndAlso _ b2 = bDollar AndAlso byts(i + 2) = bOP AndAlso _ byts(i + 3) = bD Then 'JIS_0212 'JIS Return System.Text.Encoding.GetEncoding(50220) ElseIf len - 2 > i AndAlso b2 = bOP AndAlso _ (byts(i + 2) = bB OrElse byts(i + 2) = bJ) Then 'JIS_ASC 'JIS Return System.Text.Encoding.GetEncoding(50220) ElseIf len - 2 > i AndAlso _ b2 = bOP AndAlso byts(i + 2) = bI Then 'JIS_KANA 'JIS Return System.Text.Encoding.GetEncoding(50220) End If End If End If Next i For i = 0 To len - 2 b1 = byts(i) b2 = byts(i + 1) If ((b1 >= &H81 AndAlso b1 <= &H9F) OrElse _ (b1 >= &HE0 AndAlso b1 <= &HFC)) AndAlso _ ((b2 >= &H40 AndAlso b2 <= &H7E) OrElse _ (b2 >= &H80 AndAlso b2 <= &HFC)) Then sjis += 2 i += 1 End If Next i For i = 0 To len - 2 b1 = byts(i) b2 = byts(i + 1) If ((b1 >= &HA1 AndAlso b1 <= &HFE) AndAlso _ (b2 >= &HA1 AndAlso b2 <= &HFE)) OrElse _ (b1 = &H8E AndAlso (b2 >= &HA1 AndAlso b2 <= &HDF)) Then euc += 2 i += 1 ElseIf len - 2 > i AndAlso b1 = &H8F AndAlso _ (b2 >= &HA1 AndAlso b2 <= &HFE) AndAlso _ (byts(i + 2) >= &HA1 AndAlso byts(i + 2) <= &HFE) Then euc += 3 i += 2 End If Next i For i = 0 To len - 2 b1 = byts(i) b2 = byts(i + 1) If (b1 >= &HC0 AndAlso b1 <= &HDF) AndAlso _ (b2 >= &H80 AndAlso b2 <= &HBF) Then utf8 += 2 i += 1 ElseIf len - 2 > i AndAlso _ (b1 >= &HE0 AndAlso b1 <= &HEF) AndAlso _ (b2 >= &H80 AndAlso b2 <= &HBF) AndAlso _ (byts(i + 2) >= &H80 AndAlso byts(i + 2) <= &HBF) Then utf8 += 3 i += 2 End If Next i If euc > sjis AndAlso euc > utf8 Then 'EUC Return System.Text.Encoding.GetEncoding(51932) ElseIf sjis > euc AndAlso sjis > utf8 Then 'SJIS Return System.Text.Encoding.GetEncoding(932) ElseIf utf8 > euc AndAlso utf8 > sjis Then 'UTF8 Return System.Text.Encoding.UTF8 End If Return Nothing End Function [C#] /// <summary> /// 文字コードを判別する /// </summary> /// <remarks> /// Jcode.pmのgetcodeメソッドを移植したものです。 /// Jcode.pm(http://openlab.ring.gr.jp/Jcode/index-j.html) /// </remarks> /// <param name="byts">文字コードを調べるデータ</param> /// <returns>適当と思われるEncodingオブジェクト。 /// 判断できなかった時はnull。</returns> public static System.Text.Encoding GetCode(byte [] byts) { const byte bESC = 0x1B; const byte bAT = 0x40; const byte bDollar = 0x24; const byte bAnd = 0x26; const byte bOP = 0x28; //( const byte bB = 0x42; const byte bD = 0x44; const byte bJ = 0x4A; const byte bI = 0x49; int len = byts.Length; int binary = 0; int ucs2 = 0; int sjis = 0; int euc = 0; int utf8 = 0; byte b1, b2; for (int i = 0; i < len; i++) { if (byts[i] <= 0x06 || byts[i] == 0x7F || byts[i] == 0xFF) { //'binary' binary++; if (len - 1 > i && byts[i] == 0x00 && i > 0 && byts[i - 1] <= 0x7F) { //smells like raw unicode ucs2++; } } } if (binary > 0) { if (ucs2 > 0) //JIS //ucs2(Unicode) return System.Text.Encoding.Unicode; else //binary return null; } for (int i = 0; i < len - 1; i++) { b1 = byts[i]; b2 = byts[i + 1]; if (b1 == bESC) { if (b2 >= 0x80) //not Japanese //ASCII return System.Text.Encoding.ASCII; else if (len - 2 > i && b2 == bDollar && byts[i + 2] == bAT) //JIS_0208 1978 //JIS return System.Text.Encoding.GetEncoding(50220); else if (len - 2 > i && b2 == bDollar && byts[i + 2] == bB) //JIS_0208 1983 //JIS return System.Text.Encoding.GetEncoding(50220); else if (len - 5 > i && b2 == bAnd && byts[i + 2] == bAT && byts[i + 3] == bESC && byts[i + 4] == bDollar && byts[i + 5] == bB) //JIS_0208 1990 //JIS return System.Text.Encoding.GetEncoding(50220); else if (len - 3 > i && b2 == bDollar && byts[i + 2] == bOP && byts[i + 3] == bD) //JIS_0212 //JIS return System.Text.Encoding.GetEncoding(50220); else if (len - 2 > i && b2 == bOP && (byts[i + 2] == bB || byts[i + 2] == bJ)) //JIS_ASC //JIS return System.Text.Encoding.GetEncoding(50220); else if (len - 2 > i && b2 == bOP && byts[i + 2] == bI) //JIS_KANA //JIS return System.Text.Encoding.GetEncoding(50220); } } for (int i = 0; i < len - 1; i++) { b1 = byts[i]; b2 = byts[i + 1]; if (((b1 >= 0x81 && b1 <= 0x9F) || (b1 >= 0xE0 && b1 <= 0xFC)) && ((b2 >= 0x40 && b2 <= 0x7E) || (b2 >= 0x80 && b2 <= 0xFC))) { sjis += 2; i++; } } for (int i = 0; i < len - 1; i++) { b1 = byts[i]; b2 = byts[i + 1]; if (((b1 >= 0xA1 && b1 <= 0xFE) && (b2 >= 0xA1 && b2 <= 0xFE)) || (b1 == 0x8E && (b2 >= 0xA1 && b2 <= 0xDF))) { euc += 2; i++; } else if (len - 2 > i && b1 == 0x8F && (b2 >= 0xA1 && b2 <= 0xFE) && (byts[i + 2] >= 0xA1 && byts[i + 2] <= 0xFE)) { euc += 3; i += 2; } } for (int i = 0; i < len - 1; i++) { b1 = byts[i]; b2 = byts[i + 1]; if ((b1 >= 0xC0 && b1 <= 0xDF) && (b2 >= 0x80 && b2 <= 0xBF)) { utf8 += 2; i++; } else if (len - 2 > i && (b1 >= 0xE0 && b1 <= 0xEF) && (b2 >= 0x80 && b2 <= 0xBF) && (byts[i + 2] >= 0x80 && byts[i + 2] <= 0xBF)) { utf8 += 3; i += 2; } } if (euc > sjis && euc > utf8) //EUC return System.Text.Encoding.GetEncoding(51932); else if (sjis > euc && sjis > utf8) //SJIS return System.Text.Encoding.GetEncoding(932); else if (utf8 > euc && utf8 > sjis) //UTF8 return System.Text.Encoding.UTF8; return null; } 次にこのメソッドの使い方を示します。このサンプルでは、TextBox1にテキストファイルのパスを入力し、Button1をクリックすると、テキストファイルの文字コードを調べ、デコードし、RichTextBox1にその内容を表示しています。 [VB.NET] 'Button1のクリックイベントハンドラ Private Sub Button1_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Button1.Click 'テキストファイルを開く Dim fs As New System.IO.FileStream(TextBox1.Text, _ System.IO.FileMode.Open, System.IO.FileAccess.Read) Dim bs(fs.Length - 1) As Byte 'byte配列に読み込む fs.Read(bs, 0, bs.Length) fs.Close() '文字コードを取得する Dim enc As System.Text.Encoding = GetCode(bs) 'デコードして表示する RichTextBox1.Text = enc.GetString(bs) End Sub [C#] //Button1のクリックイベントハンドラ private void Button1_Click(object sender, System.EventArgs e) { //テキストファイルを開く System.IO.FileStream fs = new System.IO.FileStream( TextBox1.Text, System.IO.FileMode.Open, System.IO.FileAccess.Read); byte[] bs = new byte[fs.Length]; //byte配列に読み込む fs.Read(bs, 0, bs.Length); fs.Close(); //文字コードを取得する System.Text.Encoding enc = GetCode(bs); //デコードして表示する RichTextBox1.Text = enc.GetString(bs); } 第三者の作成したクラス、コードを使う方法またクラスとして、G-PROJECTさんで「TextEnc」という文字コードの判別、変換を行うクラスを公開されています。 さらに雅階凡さんが「文字コードの判定」で文字コードの判定に関する詳しい説明と、サンプルコードを公開されています。 mlang.dllを使う方法外部DLLに頼る方法としては、mlang.dllのIMultiLanguage2::DetectInputCodepageを使用する方法があります。文字コードの判別が必要な大半のアプリで、これが使われているのではないかと思います。(Internet Explorer 5以上で使用できます。) 以下に.NETでIMultiLanguage2::DetectInputCodepageメソッドを使うための方法を簡単に説明します。 mlang.dllを使うため、Visual Studioの「参照の追加」でCOMを探してみても、mlang.dllは通常見つかりません。これは、mlang.dllにタイプライブラリがないためです。よってまず"MIDL.EXE"を使用して、"MLang.Idl"から"MLang.tlb"を作成し、これを"regtlib.exe"を使って登録します。 "MIDL.EXE"の位置は、VS.NET 2003をインストールしており、そのフォルダが 同じように、"MLang.Idl"の位置は、 これで「参照の追加」の「COM」で"mlang.dll"(MultiLanguage Object Model)を選択できるようになります。 補足:Visual Studioを使わない場合は、"Tlbimp.exe"を使ってinterop assembly(相互運用機能アセンブリ)を作成できます。 これで、DetectInputCodepageメソッドを使う準備ができました。次にDetectInputCodepageメソッドを使って文字コードを判別するサンプルを紹介します。このサンプルでも先と同じく、TextBox1にテキストファイルのパスを入力し、Button1をクリックすると、テキストファイルの文字コードを調べ、デコードし、RichTextBox1にその内容を表示しています。 [VB.NET] 'Button1のクリックイベントハンドラ Private Sub Button1_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Button1.Click 'テキストファイルを開く Dim fs As New System.IO.FileStream(TextBox1.Text, _ System.IO.FileMode.Open, System.IO.FileAccess.Read) Dim bs(fs.Length - 1) As Byte 'byte配列に読み込む fs.Read(bs, 0, bs.Length) fs.Close() 'sbyte型配列に変換する Dim sbs(bs.Length - 1) As System.SByte System.Buffer.BlockCopy(bs, 0, sbs, 0, bs.Length) '文字コードを判別する Dim enc As System.Text.Encoding = DetectEncoding(sbs) 'デコードして表示する RichTextBox1.Text = enc.GetString(bs) End Sub ''' <summary> ''' IMultiLanguage2.DetectInputCodepageを使って文字コードを判別する ''' </summary> ''' <param name="sbyts">文字コードを調べるデータ</param> ''' <returns>適当と思われるEncodingオブジェクト。</returns> Public Shared Function DetectEncoding(ByVal sbyts() As System.SByte) _ As System.Text.Encoding '準備 Dim ml As New MultiLanguage.CMultiLanguageClass Dim scores As Integer = 10 Dim detects(scores - 1) As MultiLanguage.tagDetectEncodingInfo Dim len As Integer = sbyts.Length '文字コードを判別 ml.DetectInputCodepage(Convert.ToUInt32(0), Convert.ToUInt32(0), _ sbyts(0), len, detects(0), scores) '一番初めのtagDetectEncodingInfoのEncodingを返す Return System.Text.Encoding.GetEncoding( _ Convert.ToInt32(detects(0).nCodePage)) End Function [C#] //Button1のクリックイベントハンドラ private void Button1_Click(object sender, System.EventArgs e) { //テキストファイルを開く System.IO.FileStream fs = new System.IO.FileStream( TextBox1.Text, System.IO.FileMode.Open, System.IO.FileAccess.Read); byte[] bs = new byte[fs.Length]; //byte配列に読み込む fs.Read(bs, 0, bs.Length); fs.Close(); //sbyte型配列に変換する sbyte[] sbs = new sbyte[bs.Length]; System.Buffer.BlockCopy(bs, 0, sbs, 0, bs.Length); //文字コードを判別する System.Text.Encoding enc = DetectEncoding(sbs); //デコードして表示する RichTextBox1.Text = enc.GetString(bs); } /// <summary> /// IMultiLanguage2.DetectInputCodepageを使って文字コードを判別する /// </summary> /// <param name="sbyts">文字コードを調べるデータ</param> /// <returns>適当と思われるEncodingオブジェクト。</returns> public static System.Text.Encoding DetectEncoding(sbyte [] sbyts) { //準備 MultiLanguage.IMultiLanguage2 ml = new MultiLanguage.CMultiLanguageClass(); int scores = 10; MultiLanguage.tagDetectEncodingInfo[] detects = new MultiLanguage.tagDetectEncodingInfo[scores]; int len = sbyts.Length; //文字コードを判別 ml.DetectInputCodepage( 0, 0, ref sbyts[0], ref len, ref detects[0], ref scores); //一番初めのtagDetectEncodingInfoのEncodingを返す return System.Text.Encoding.GetEncoding( (int) detects[0].nCodePage); } DetectInputCodepageメソッドにより、複数の候補がtagDetectEncodingInfoとして取得されることがありますが、0番目のtagDetectEncodingInfoを採用するようにしています。また上記の方法では判別するデータをsbyte型配列として指定するため、byte型配列をsbyte型配列に変換しています。
(この記事は、「.NETプログラミング研究 第43号」で紹介したものを基にしています。) |
|
Copyright 2002-2008 DOBON!. All rights reserved.
|