2011-01-17

Phone 7 で シフトJIS を読みたい

昨日まで知らなかったのですが Windows Phone 7 というか Silverlight には シフトJIS のエンコーディングが無いそうです。

MSDN にはこんな風に書かれていました。()は適当に意訳
The Encoding class is primarily intended to convert between these three different Unicode encodings. In addition, you can derive from the Encoding class to create a custom encoding that is not supported by the .NET Framework for Silverlight.
(Encoding クラスってのは異なるエンコーディング同士の変換してるだけだから、サポートされてないエンコーディングが欲しければ自分で作れば?)
If you require an encoding that is not present in the .NET Framework for Silverlight, you can also use ASP.NET and the .NET Framework to perform the encoding or decoding on a Web server.
(Silverlight でサポートされてないエンコーディングが欲しければ ASP.NET 使ってサーバ側でエンコード・デコードするって手もあるよ)

見事な投げっぷりですが、いやいや シフトJIS 読めないと困りませんか? と言うか僕が今困ってます。

自分専用と割り切ってサーバで文字コード変換プロキシ立ててもいいんですが、できればアプリは公開したい...でもサーバの資源は限られる...という事で前者のカスタムエンコーディングクラス案で行ってみようと思います。

まずは変換方法ですが、文字コードまわりに明るくないのでひとまず力業で行きます。
Unicode Consortiumから JIS X 0201 (1976)JIS X 0208 (1990) の一覧を持ってきて変換テーブルを作りました。
private static Dictionary<byte, char> Jis0201Table = new Dictionary<byte, char>
{
    { 0x20, '\u0020' },
    { 0x21, '\u0021' },
    { 0x22, '\u0022' },
    { 0x23, '\u0023' },
    { 0x24, '\u0024' },
    { 0x25, '\u0025' },

private static Dictionary<ushort, char> Jis0208Table = new Dictionary<ushort, char>
{
    { 0x8140, '\u3000' },
    { 0x8141, '\u3001' },
    { 0x8142, '\u3002' },
    { 0x8143, '\uFF0C' },
    { 0x8144, '\uFF0E' },
    { 0x8145, '\u30FB' },

続けて System.Text.Encoding を継承したクラスを作ってデコード関係と思われるメソッドで 1バイトごとに置き換えていきます。
public class SjisEncoding : System.Text.Encoding
{
    public override string WebName
    {
        get{ return "Shift_JIS"; }
    }
    public char FallbackChar { get; set; }

    // デコード
    public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
    {
        int outChars = 0;

        for (int i = 0; i < byteCount; i++)
        {
            byte b = bytes[byteIndex + i];
            if ((0x20 <= b && b <= 0x80) || (0xA0 <= b && b <= 0xDF))
            {
                if (Jis0201Table.ContainsKey(b))
                {
                    chars[charIndex + outChars] = Jis0201Table[b];
                }
                else
                {
                    chars[charIndex + outChars] = FallbackChar;
                }
                outChars++;
            }
            else if (((0x81 <= b && b <= 0x9F) || (0xE0 <= b && b <= 0xFF)) && i + 1 < byteCount)
            {
                ushort s = (ushort)(((ushort)b << 8) | (ushort)bytes[byteIndex + i + 1]);
                i++;
                if (Jis0208Table.ContainsKey(s))
                {
                    chars[charIndex + outChars] = Jis0208Table[s];
                }
                else
                {
                    chars[charIndex + outChars] = FallbackChar;
                }
                outChars++;
            }
            else
            {
                chars[charIndex + outChars] = (char)b;
                outChars++;
            }
        }

        return outChars;
    }

このクラスを WebClient.Encoding に渡してみたところ、ちゃんと読めていました。
ヒャッハーやったぜ。
SjisEncoding sjisEncoding   = new SjisEncoding();
WebClient    wc             = new WebClient();

wc.Encoding                 = sjisEncoding;
wc.DownloadStringCompleted += (s, e) =>
    {
        if (!e.Cancelled && null == e.Error)
        {
            ParseBbsMenu(e.Result);
        }
    };
wc.DownloadStringAsync(new Uri(http://menu.2ch.net/bbsmenu.html));

ただ、変換テーブルがほんの 7,000行ほどあって 貧弱なノート PC では Visual Studio が止まったように遅くなります。
そもそも、こんなネタみたいな力業以外に変換方法が無いか調べてみないといけませんね。

5 件のコメント:

匿名 さんのコメント...

素晴らしいですね。是非、ソースコードを全てUPして欲しいです。

kazuaki さんのコメント...

こんばんは。
結局、デコードできたところで満足して放置していました...
エンコード部分もできたらライブラリとして公開するようにしますね。

Unknown さんのコメント...

WindowsPhone用2chブラウザNTRというアプリを作っているShinjiJapanというものです。
参考にさせていただきました。非常に助かりました。

kazuaki さんのコメント...

ShinjiJapan さん こんにちは。

NTR 使わせて頂きました、どこかのアプリとは異次元の速度で驚いています(笑
自分は真っ先に ListBox の使用を諦めたくちなので、他の 2ch ブラウザを見るたびに ちょっと羨ましくなります。

今後ともよろしくお願いします。

Unknown さんのコメント...

mango以降の開発だったのでたまたま最初からデキのいいListBoxが使えてタイミングよかっただけです。
WakamurasakiはWP上で一覧表示する際の問題点を一挙に解消した素晴らしいUIだと思います。

こちらこそよろしくお願いします。