none
Unicode→Shift_JIS変換時に、Shift_JISに存在しない文字を数値参照文字にする方法 RRS feed

  • 質問

  • Shift_JIS形式のテキストファイルを編集するアプリケーションを作成中で、Shift_JISに存在しない文字も読み書きしたいと考えております。
    その為、保存時にShift_JISに存在しない文字を数値参照文字に変換したいのですが、.NET Frameworkにそのような変換を行ってくれるクラスはあるのでしょうか?

    存在しない場合、代替手段としてどのような手段が考えられますでしょうか?

    具体的には、以下のような処理をする予定です。
     ①Shift_JIS形式のテキストファイルを読み込む
     ②数値参照文字をデコード(これはSystem.Web.HttpUtility.HtmlDecodeメソッドでできました)
     ③Unicodeで編集
     ④Unicodeに存在し、Shift_JISに存在しない文字を数値参照文字に変換 ←ここが分かりません
     ⑤変換後のテキストを保存

    以上、お手数ですが、宜しくお願いします。




    2017年1月12日 10:01

回答

  • 一案ですが。

    1. Encoding.GetEncodingで、EncoderExceptionFallbackを渡してEncodingを取得する。
    2. Encoding.GetEncoder()で取得できるEncoderを使って、入力文字列をchar1つずつ、順次Convertでbyte[]に変換する。
    3. Convertが成功した場合、出力先のFileStreamに変換されたbyte[]を出力する。
    4. Convertで例外が出た場合、出力先のFileStreamに、"&#" + (int)character + ";" をbyte[]化したものをFileStreamに出力する。

    とか。

    まじめに実装するなら、EncoderFallback/EncoderFallbackBufferを自前で実装し、Fallback時に数値文字参照を返すようにする、でしょうかね。

    数値文字参照を使うのなら、元のテキストに生の & を使用できなくなるのでこれのエスケープも考えないと行けませんね。

    あと、HtmlDecodeを使うと、HTMLの実体参照類も勝手にデコードされてしまいますが大丈夫ですか?(&さえ正しくエスケープされてれば大丈夫だったかな…)。なお、非webアプリ用に、System.Net.WebUtilityクラスの方にメソッドが用意されています。

    • 回答としてマーク しげおQk 2017年1月12日 21:03
    2017年1月12日 11:18
  • 一例です。

    private string convertToShiftJIS(string str)
    {
        string output = "";
        foreach(char c in str)
        {
            if (isUnicodeChar(c))
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("&#x{0:x};", (int)c);
                output += sb.ToString();
            }
            else
            {
                output += c;
            }
        }
        return output;
    }
    
    private bool isUnicodeChar(char c)
    {
        string checkString = c.ToString();
        byte[] translateBuffer = Encoding.GetEncoding("shift_jis").GetBytes(checkString);
        string translateString = Encoding.GetEncoding("shift_jis").GetString(translateBuffer);
        return (checkString != translateString.ToString());
    }


    もっと効率の良い方法があるかもしれませんが、、

    参考サイト: 
    http://acha-ya.cocolog-nifty.com/blog/2010/12/unicode-ef79.html

    2017年1月12日 11:31

すべての返信

  • 一案ですが。

    1. Encoding.GetEncodingで、EncoderExceptionFallbackを渡してEncodingを取得する。
    2. Encoding.GetEncoder()で取得できるEncoderを使って、入力文字列をchar1つずつ、順次Convertでbyte[]に変換する。
    3. Convertが成功した場合、出力先のFileStreamに変換されたbyte[]を出力する。
    4. Convertで例外が出た場合、出力先のFileStreamに、"&#" + (int)character + ";" をbyte[]化したものをFileStreamに出力する。

    とか。

    まじめに実装するなら、EncoderFallback/EncoderFallbackBufferを自前で実装し、Fallback時に数値文字参照を返すようにする、でしょうかね。

    数値文字参照を使うのなら、元のテキストに生の & を使用できなくなるのでこれのエスケープも考えないと行けませんね。

    あと、HtmlDecodeを使うと、HTMLの実体参照類も勝手にデコードされてしまいますが大丈夫ですか?(&さえ正しくエスケープされてれば大丈夫だったかな…)。なお、非webアプリ用に、System.Net.WebUtilityクラスの方にメソッドが用意されています。

    • 回答としてマーク しげおQk 2017年1月12日 21:03
    2017年1月12日 11:18
  • 一例です。

    private string convertToShiftJIS(string str)
    {
        string output = "";
        foreach(char c in str)
        {
            if (isUnicodeChar(c))
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("&#x{0:x};", (int)c);
                output += sb.ToString();
            }
            else
            {
                output += c;
            }
        }
        return output;
    }
    
    private bool isUnicodeChar(char c)
    {
        string checkString = c.ToString();
        byte[] translateBuffer = Encoding.GetEncoding("shift_jis").GetBytes(checkString);
        string translateString = Encoding.GetEncoding("shift_jis").GetString(translateBuffer);
        return (checkString != translateString.ToString());
    }


    もっと効率の良い方法があるかもしれませんが、、

    参考サイト: 
    http://acha-ya.cocolog-nifty.com/blog/2010/12/unicode-ef79.html

    2017年1月12日 11:31
  • 高速化が要るなら、64k個の変換可否テーブルを用意して1文字ずつ調べる手でしょうか。

    なお、CharはUTF-16なので、サロゲートペアが出現する可能性があります。
    その場合、文字体参照ではサロゲートペアでの表現は禁止なので、いったんUCS4に合成してから変換する必要が出ます。



    jzkey

    2017年1月12日 15:30
  • この方法で出来ました!

    ありがとうございました!

    2017年1月12日 21:10
  • 返信ありがとうございます。

    >HtmlDecodeを使うと、HTMLの実体参照類も勝手にデコードされてしまいますが大丈夫ですか?
     とりあえず今扱ってるデータだと問題なさそうです。 

    >なお、非webアプリ用に、System.Net.WebUtilityクラスの方にメソッドが用意されています。
     そんなメソッドがあったんですね。 わざわざSystem.Webを追加するのもアレなので、こちらを使わせていただきます。
    2017年1月12日 21:17