seraphyの日記

日記というよりは過去を振り返るときのための単なる備忘録

CharsetEncoder/Decoderの不可解

JAVAの文字列はUNICODEだが、これをNLS環境下のORACLEのようなデータベースに格納する場合の文字長判定をデータベースへのアクセス前に確認するために一旦、文字列をバイト配列に変換して長さを判定しなければならない。
Weblogic6.0/6.1ではJ2SE1.3なので、この手のチェックは、かなり泥臭くなる。というのも、マルチバイト文字に変換したあとに、単純に切って文字長を判定すると、その末尾がLeadByteだけで切れてしまったりして不正な文字になる可能性がある。
現在はJ2SE1.4環境であるのならば、この手の変換にCharsetEncoder/Decoderを使えばスマートで合理的な解決ができるのではないか、と試みた。
なるほど、CharsetEncoder/Decorderのencode/decodeメソッドで変換するのは非常に容易だし、不正文字チェックも働くし、言うことない感じじゃないの。
…と、思ったら書いたテストコードの1つが動かない。
バイト配列に1文字だけセットして、それをUNICODEに戻そうとすると、必ず空文字になる。
2バイト以上ならば問題ないのに、どうゆうことだ?
悩んでJ2SE1.4のソースを追うと…。
バイトからUNICODEに変換する場合の1バイトあたりの平均文字数をaverageCharsPerByteメソッドによってfloat値として取得して計算し、あらかじめ文字バッファを確保する仕組みになっていたが、MS932(csWindows31j)のようなコードでは、2バイトで1文字、すなわち、1バイトあたり0.5文字である。そして、0.5 x 1バイト = 0.5文字、というバッファを確保しなければならないのだが、これをバッファ確保の整数値を出すためにintで切り捨てるため、0文字となる。

そして「バッファサイズが0文字のときはなにもせず戻る」という、とんでもないコードが書かれていたのだ!!

マジか!?
勘弁してくれ…。
本当にJ2SEは30万以上のテストコードを通過してきたのか?
使った初日に判明するパグだぞ!?
ここは、小数点は無条件に切り上げるコードが必要だろうな。
たしかにJ2SE1.4が出たばかりのころ、日本語エンコーディングがズタボロだと酷評されていたから、やはり、J2SE1.4からの新しいメカニズムnioにはバグが沢山あったのだろう。

これで、このコンビニエンスメソッドが使えないことが判明。
バグパレードに載っているかどうかは調べていないが、現在の本番環境で使えないことは確定したので、そんなことはどうでもいい。

そのあと、CharsetDecoder#decode( ByteBuffer, CharBuffer, bool)を使って自分でバッファ管理しながら変換することで問題は容易に回避できたが、その不可解な事象を調べるのに2時間は潰した。
はじめて使うクラスだったので自分の使い方が不味いのではないか、とドキュメントを隅々まで読んで、いろいろ試したので、どっ、と疲れた感じだよ。

あとは変数展開のコードを書き直そうと思ったけれど、なんだか眠くなってきて進みがあきらかに低下する。
とりあえず、終電間近になったので帰る。