rm /blog

IT系技術職のおっさんがIT技術とかライブとか日常とか雑多に語るブログです。* 本ブログに書かれている内容は個人の意見・感想であり、特定の組織に属するものではありません。/All opinions are my own.*

【Java】文字エンコーディングの変換(UTF-8⇒SJIS)

テキストファイルのエンコーディングUTF-8⇒Shift-JIS(MS932)に変換する自作プログラム。 サクラエディタとかのテキストエディタとかに標準で備わってはいるが 元のテキストファイルが重いとメモリ食うので、自作しました。 自分専用の色が強いですが。

 


 引数にUTF-8エンコーディングされたテキストファイルのファイルパス(絶対パス)を与えると、 2つのファイルを吐きだします。

1.拡張子手前に「_NON_CONV」がついたファイル

2.拡張子手前に「_UTF2MS932」がついたファイル

出力後の拡張子は「.txt」固定。 なお引数無で起動すると速攻で処理終了します。 下記のようなバッチファイルを作成しておいて、それ叩いたほうが良いですね。

 set /p target_file=  java EncodingConvertSample "%target_file%"

※見ればわかると思いますが上記はWindows用です

一般的には、別エンコード(用の文字)への変換は

 String str = "あ";  String dst = new String(str.getBytes("MS932"),"MS932");

で行います。

これで文字"あ"をMS932用のエンコーディングにすることができます。 javaは内部的には全ての文字をUnicodeで保持するので、 String型変数dstの中身もUnicode文字コード値が格納されていますが、 「MS932で出力する用」に中身の値を入れ替えたと解釈しておきます。 多くの場合はこれで事が済みますが よく問題になる「波ダッシュ問題」はこれだとうまくいきません。 (下記詳細)


一応、このプログラムは javaでよく話題になる「波ダッシュ問題」も考慮にしています。 どこででも言われていることですが、 要するに下記の文字のUTF8⇒SJISマッピングが変なことが原因なわけですね。 デフォルトだとSJIS側に未定義の文字にマッピングされてしまうので、 「見つからない」を意味する"?"になってしまいます。 上述した別エンコードへの変換前に下記ような変換をかけることで これを防止するのが一般的なやり方として広く知られています。

原因となる文字コード(文字)変換できる文字コード(文字)変換前文字の呼び名変換後文字の呼び名

Ux301c(〜) Uxff5e(~) WAVE DASH FULLWIDTH TILDE
Ux2016(‖) Ux2225(∥) DOUBLE VERTICAL LINE PARALLEL TO
Ux2212(−) Uxff0d(-) MINUS SIGN FULLWIDTH HYPHEN-MINUS
Ux00a2(¢) Uxffe0(¢) CENT SIGN FULLWIDTH CENT SIGN
Ux00a3(£) Uxffe1(£) POUND SIGN FULLWIDTH POUND SIGN
Ux00ac(¬) Uxffe2(¬) NOT SIGN FULLWIDTH NOT SIGN
Ux2014(—) Ux2015(―) EM DASH FHORIZONTAL BAR

なので私は個人的にはこのての問題は 厳密にいうと文字化けというより「変換失敗(できない)」だと思いますが 文字化けのほうが通りがいいのでそう呼ぶことにします。 上記の考慮はプログラム内のtoSJISというメソッドで実装しています。 このプログラムは(個人的な比較検証を目的としたものですが) toSJISを介したものと介していないものをそれぞれ同階層に出力します。 上で挙げた出力ファイル2つのうち、 1.はtoSJISを介していないもの 2.はtoSJISを介しているもの の違いです。 実際に波ダッシュを変換かけてみると、 1.では結果が"?"になりますが2.では"~"になってくれます。


このプログラムは 個人的な目的を加味したこともあってテキストtoテキストの変換ですが、 実際の業務システムとしては 例えばUTF-8のDBからデータを取得して、 Windowsのクライアント端末にCSVSJIS)でダウンロードするようなケースで このての問題に良く遭遇する気がします。 というか実際遭遇しました。 あとは外部システムからのHTTPリクエスト(UTF-8)を 昔からあるシステム(EUC-JP)に取り込むときとか。 当然ここでも同じ考慮をする必要があります。 なので重要なのはtoSJISの部分なわけですね。


この問題はUnicodeから別エンコードへの変換のマッピングが変なせいで 変換先に存在しない文字を割り当てていることが原因で起きるわけですが、 そもそもUnicodeの方がShift_JISより文字数が多いので、 マッピングの不正がなかったとしても変換できない文字というのが存在します。

Unicodeは全世界の文字を扱うことを目的にして作られたらしいので、 レアなところだとアラビア語とかは全てこの対象です。 日本語で有名所では「𣘺(Ux2363a)」(橋-はし-の旧字体)とかはそれに該当します。 ⇒WindowsIMEパッドを開いて「Shift_JIS」の列が-になっているのは全てこれに該当します。

これらの文字は、変換するも何も そもそもShift_JIS側に存在しないのでどうしようもありません。 toSJISメソッドはその考慮まではしておらず、 あくまで上に挙げた7文字だけを救う手だてにすぎません。 これらの文字まで考慮するなら、変換後に1文字1文字チェックしていって 変換後"?"(Ux003f)になってたら全角ハテナ"?"(Uxff1f)に置き換えるとか、 やり方はいろいろありますが、 普通の?と区別つかなくなりますし、 内容によっては文字列の意味が不明になるので 扱いづらいのが正直なところです。

また、これはUnicodeに限った話じゃなく、 Shift_JIS関連ではMS932⇒iso-2022-jpの変換でも同じようなことが発生し得ます。 MS932はShift_JISMicrosoft拡張文字なので、 いわゆる機種依存文字(①とか)が扱えますが iso-2022-jp(電子メールでよく使うエンコード)は機種依存文字が扱えないので 何も考えずに"①"とかうって送信すると 受け取り側で"?"になって読み取れないということがよくあります。 ①なら1に無理やり置き換えるとかできなくもないですが、 上記も含めて全てのケースに対して自前で対処することはできませんし、 各エンコード毎の事情があるもんだということを 事前に知識として持っておく必要があるでしょうね。


開発前にはこういう背景をきちんと把握して顧客と調整しておく必要があるんでしょうが 事象が意外にレアなもんだから初期段階から割と置き去りにされてる可能性が高く、 後になって発覚すると部品修正だからテストが大変なので無償ではできません 等というとそもそも事前に説明があったんですかと顧客から怒られて上位層を巻き込む問題に… とかいうことにならないようにしたいものです。 (※別に実体験ではありませんよ)