VB.NETでも独自のエンコーディングを実装したい(前編)
はじめに
あれを今回はVB.NETでやるというという誰も望まないし誰も得しない企画です。
符号化文字集合及び文字符号化方式はPython版から流用します。
ドキュメントは大切に
とりあえず.NETでの文字コードはここから始めればいいのかな。
.NETの文字コード関係はSystem.Text
名前空間に集められているようです。
パッと見た感じでは
Encoder
・Decoder
の実装Encoder
・Decoder
を用いてEncoding
を実装
といった流れです。
また、ユーザはエンコーダ・デコーダに対してフォールバック戦略を指定することができます。
逆に言うとエンコードの開発者はユーザに対してフォールバック戦略を選択できる手段を提供しないといけないってことです。
これはPythonではerrors
オプションで指定していたエラー処理スキームにあたります。
.NET での文字エンコード | Microsoft Docs
フォールバック戦略はエンコード時もしくはデコード時に変換できない文字・バイト列を検出した時にエンコーダ・デコーダが取りうる挙動を規定するものです。
- 最適フォールバック
- 置換フォールバック
- ターゲットエンコードに厳密な一致がなくマップできる適切な文字も無い場合は諦めて適当な文字で置き換える戦略です。
- 例外フォールバック
最適な戦略はターゲットエンコードによって変わるので明確に文書化はしないそうです。
エンコーダ・デコーダの実装やエンコーディングの実装もサンプルコードや詳しい説明が無いんですよね。
もともとユーザが拡張するなんてよほどのことがない限りやりませんからね。
自作したエンコーディングをEncoding.GetEncoding()
からエンコード名で呼び出す方法も公開されてないっぽいです。
とりあえず今回はフォールバックについて一通り確認して終わりたいです。
フォールバック
まぁ上ではフォールバックストラテジとか書いちゃってますけど、つまるところエンコーダ・デコーダが正常に処理を続行出来ない時にどうさせるかって問題です。
Imports System Imports System.Text Module Module1 Sub Main() FallbackTest( Encoding.GetEncoding("us-ascii"), "hello Å こんにちは") FallbackTest( Encoding.GetEncoding( "us-ascii", New EncoderExceptionFallback(), New DecoderExceptionFallback() ), "hello Å こんにちは") FallbackTest(Encoding.GetEncoding( "us-ascii", New EncoderReplacementFallback("*"), New DecoderReplacementFallback("*") ), "hello Å こんにちは") FallbackTest(Encoding.GetEncoding( "Shift_JIS"), "hello Å こんにちは") FallbackTest( Encoding.GetEncoding( "Shift_JIS", New EncoderExceptionFallback(), New DecoderExceptionFallback() ), "hello Å こんにちは") FallbackTest(Encoding.GetEncoding( "Shift_JIS", New EncoderReplacementFallback("*"), New DecoderReplacementFallback("*") ), "hello Å こんにちは") End Sub Sub FallbackTest(encoding As Encoding, target As String) Console.WriteLine("Encoding name : {0}", encoding.EncodingName) Console.WriteLine( "Encoding Fallback name : {0}", encoding.EncoderFallback) Console.WriteLine("target string : {0}", target) Try Dim bytes = encoding.GetBytes(target) For Each byt In bytes Console.Write("{0:X2} ", byt) Next Console.WriteLine() Console.WriteLine( "Decoded target : {0}", New String(encoding.GetChars(bytes))) Catch ex As EncoderFallbackException Console.Write("Exception Occur: ") If ex.IsUnknownSurrogate Then ' 不正なサロゲートを受け取ったらこっち Console.WriteLine( "Unable to encode surrogate pair" & _ " 0x{0:X4} 0x{1:X3} at index {2}.", Convert.ToUInt16(ex.CharUnknownHigh), Convert.ToUInt16(ex.CharUnknownLow), ex.Index) Else ' それ以外 Console.WriteLine( "Unable to encode 0x{0:X4} at index {1}.", Convert.ToUInt16(ex.CharUnknown), ex.Index) End If End Try Console.WriteLine() End Sub End Module
こんなサンプルコードを参考にするくらいならmsdnのサンプルコードを見てくださいって感じですが、まぁ一応こんな感じです。 つーか弊社もmsdnのコードを参考にしました。(見覚えのあるコードがちらほら)
そんで出力がこれ
Encoding name : US-ASCII Encoding Fallback name : System.Text.EncoderReplacementFallback target string : hello A こんにちは 68 65 6C 6C 6F 20 3F 20 3F 3F 3F 3F 3F Decoded target : hello ? ????? Encoding name : US-ASCII Encoding Fallback name : System.Text.EncoderExceptionFallback target string : hello A こんにちは Exception Occur: Unable to encode 0x00C5 at index 6. Encoding name : US-ASCII Encoding Fallback name : System.Text.EncoderReplacementFallback target string : hello A こんにちは 68 65 6C 6C 6F 20 2A 20 2A 2A 2A 2A 2A Decoded target : hello * ***** Encoding name : 日本語 (シフト JIS) Encoding Fallback name : System.Text.InternalEncoderBestFitFallback target string : hello A こんにちは 68 65 6C 6C 6F 20 41 20 82 B1 82 F1 82 C9 82 BF 82 CD Decoded target : hello A こんにちは Encoding name : 日本語 (シフト JIS) Encoding Fallback name : System.Text.EncoderExceptionFallback target string : hello A こんにちは Exception Occur: Unable to encode 0x00C5 at index 6. Encoding name : 日本語 (シフト JIS) Encoding Fallback name : System.Text.EncoderReplacementFallback target string : hello A こんにちは 68 65 6C 6C 6F 20 2A 20 82 B1 82 F1 82 C9 82 BF 82 CD Decoded target : hello * こんにちは
us-asciiとみんな大好きShift_JISででエンコしてデコードしました。
フォールバック戦略を指定しなかった時はus-asciiは置換フォールバックを選択していますが、 Shift_JISでは最適フォールバックを選択しています。 そのため、Å(U+00C4)の『Aの上にちっさい丸がくっついている文字』*1はus-asciiでは置換フォールバックのデフォルトの置換文字である?に置き換わっています。 対してShift_JISでは最適フォールバックが選択されているため、この時は大文字のAが最適な代替と判断されそれに置き換わっています。
余談ですが、日本語版Windowsのコマンドプロンプトのコードページは932(MSのShift_JISの実装)であるので、Åが正しく表現できません。 そのためか、上の出力のtarget stringの表示のÅがAになっちゃってます。 これもおそらく最適フォールバックの結果でしょう。
例外フォールバックはどちらも認識できない文字を受けとった瞬間に例外をスローしています。
置換フォールバックでは認識できない文字はデフォルトもしくは指定した文字に置き換えて出力をしています。 明示的に置換フォールバックを指定した方のShift_JISでは最適フォールバックと異なり認識できない文字は無理に代替を置かずに指定した文字に置き換えているのがわかります。
ちなみに最適フォールバックでも適切な代替がないときは置換フォールバックと同じ挙動をします。
FallbackTest(Encoding.GetEncoding( "Shift_JIS"), String.Format("{0}{1}", "hello Å こんにちは ", ChrW(&H2681)))
Encoding name : 日本語 (シフト JIS) Encoding Fallback name : System.Text.InternalEncoderBestFitFallback target string : hello A こんにちは ? 68 65 6C 6C 6F 20 41 20 82 B1 82 F1 82 C9 82 BF 82 CD 20 3F Decoded target : hello A こんにちは ?
指定しているのはサイコロ記号*2なんですけど、どう考えてもJIS X 0208(とNEC特殊文字とNEC選定IBM特殊文字とIBM拡張文字)の中に適切な文字は無いんで諦めて?を表示しています。
弊社も無理難題を投げられたら例外フォールバックしたいですね。 とまぁ、フォールバックに軽く触れたので今回はここまで