毎度毎度、文字コードの話は面倒である。開発のメインストリームでないことと、外部調整が必要であることが原因だと思う*1。
プラットフォームが変わるたびに、毎回検証しているような気がする。私の場合だと、毎回OSが違う。Windows Server、AIX、HP-UX、Red Hat Linux。毎回Java仮想マシン(JVM)が違う。SunのJDK(今はOracleか)、JRockit(これもOracleか)、IBMのJVM、HPのJVM。データベースエンジンは、だいたい同じ。
本当にみんな、毎回やっているのだろうか?
とりあえず、Javaで開発することは決まっている前提で、外部設計と内部設計にて、やるべき(と思っている)ことをまとめておく。
以降で挙げる例は、次のようなシステムをイメージしている*2。
- 構築対象システム
- Webアプリケーション
- バッチ
- 外部システム
- ホスト
外部設計
画面や帳票のようなユーザインターフェースであれば、外部設計時には、システムで用いる文字集合の見た目だけ*3を決定しておけばよい。例えば、次のようになる*4。
- 半角
- 全角
- JIS X 0208:1997
- 非漢字(523字)
- 第一水準漢字(2965字)
- 第二水準漢字(3390字)
- IBM拡張文字(115区〜119区)
- JIS X 0208:1997
ここで、これらの文字が、エンドユーザの端末(大概Windows)で取り扱えるかどうかを調査しておく。この例であれば簡単。日本のWindows 2000、XP、Vistaであれば、バックスラッシュやオーバーラインは対象外にすべきだろう。で、文字の一覧を(テキストで)作成して、提示する。
UnicodeにおけるBMP(基本多言語面)にある文字のみであれば、Javaとデータベースは、まず大丈夫。BMPにある文字のみしか扱ったことがないので、以降はこの前提で。
外部システムとファイル連携がある場合には、文字の見た目の集合を決めただけではいけない。ファイルに書かれているのは「文字」ではなく「バイト(列)」だからだ。そこで今度は、上記で定めた文字の集合を、連携時にどのようなバイト列で取り扱うかを決める。
「Shift_JISを用いる」、「Windows-31Jのエンコード方式を用いる」などとしてもよいが、きちんとやるなら、文字の見た目とバイト列の一覧表を付け合わせるのがよい。これであれば、お互いの勘違いもなくなるし、文字に対して複数のバイト表現が定義されている場合(今回の例であれば、IBM拡張文字と、13区に定義されているNEC特殊文字)にも対応できる。
連携先がホストである場合には、MQやHULFTの文字変換テーブルを用いる。この場合、まず、どちら側で変換を行うか決定する。
こちら側で変換するとなったら、「文字の見た目、変換前のバイト列、変換後のバイト列」の一覧を作成しておく。これと、連携先システムにおける「文字の見た目、バイト列」の一覧と付け合わせを行いたい。バイト列が不一致のところは、MQやHULFTの設定にて、変換テーブルを変更する。
検証するための環境(MQやHULFT)がなければ、課題として次フェーズへ持ち越す。
「文字の見た目、変換前のバイト列」の一覧は、Javaで検証しながら作成する。この時、Javaの内部コードとバイト表現が、1対1に対応することが望ましい。
次に、(私の環境で)Javaにおいて、1対多、多対1になる例を示す*5。
(1) 1対多の例 Ⅰ(ローマ数字の1) U+2160 <--(windows-31j)--> 0x8754 U+2160 <--(windows-31j)--- 0xFA4A (2) 多対1の例 ¢(セント記号) U+00A2 ---(windows-31j)--> 0x8191 U+FFE0 <--(windows-31j)--> 0x8191
(1)の例であれば、Javaにおける内部コード(Unicodeのコードポイント)がU+2160の場合は、Windows-31Jのエンコード方式にてエンコードすると、0x8754のバイト列になる。0x8754をデコードすると、U+2160になる。しかし、0xFA4AをデコードしてもU+2160になる。この場合、ローマ数字の1のバイト表現は、0x8754とした方がよいだろう。こうしないと、バイナリレベルで文字やファイルそのものを突合する際などに問題が発生する。
(2)の例であれば、U+00A2は、処理の入口で許容対象外としてはじくか、処理の入口でU+FFE0によせた方がよいだろう。こうしないと、Javaの内部で、同一文字かどうかを判定する際などに問題が発生する。
内部設計
構築するシステムの内部で文字をどのように取り扱うかを決める。作業量は増えるが、外部調整がなくなる分、楽になるはず。作業自体は、外部設計の場合とあまり変わらない。
詳細には残さないが、次のような表ができればよいのかな。
対象 | エンコード | 設定内容(設定対象) | 設定理由 |
---|---|---|---|
データベース | Windows-31J | JA16SJISTILDE(Oracle) | 検証結果 |
外部システムと連携するファイル(パス名) | Windows-31J | ja_JP.SJIS(Linux) | ― |
外部システムと連携するファイル(内容) | Windows-31J | windows-31j(Java) | 検証結果 |
ログファイル(パス名) | Windows-31J | ja_JP.SJIS(Linux) | ログ監視ソフト |
ログファイル(内容) | Windows-31J | windows-31j(Java) | ログ監視ソフト |
ダウンロードファイル(パス名) | UTF-8 | ja_JP.UTF-8(Linux) | IEの「ファイルのダウンロード」ダイアログ |
ダウンロードファイル(内容) | ― | ― | バイナリファイル |
検証に使うプログラムは、だいたい同じ。整理しておく。
2010/07/24追記
「Javaにおける文字コードまわりの話(5)」に、(整理し切れてはいないのだが)簡単なまとめを書いた。