JDK 1.5.0_12, ojdbc14 での話。
/** 分割開始インデックス */
static final int DIVISION_BYTE_START_INDEX = 0;
/** 分割後文字長 */
static final int DIVISION_BYTE_LENGTH_ADDR = 10;
/**
* 引数の文字列を[分割後文字長(byte)]以下にカットする。
* 扱う文字コードは MS932 とする。
*
* @param str 文字列(MS932)
* @return String [分割後文字長(byte)]以下の文字列
*/
String getDivideStringByBytes(String str) {
if(str == null) {
return null;
}
String result = "";
try {
// バイト配列化
byte[] byteAddr = address.getBytes("MS932");
if(byteAddr.length > DIVISION_BYTE_START_INDEX) {
// byte文字列が分割開始インデックス以上の長さである
if(byteAddr.length > DIVISION_BYTE_START_INDEX + DIVISION_BYTE_LENGTH_ADDR) {
// byte文字列が分割したい文字長以上の長さである
result = new String(byteAddr,
DIVISION_BYTE_START_INDEX,
DIVISION_BYTE_LENGTH_ADDR,
"MS932").trim();
} else {
// 必要文字長に満たない場合
result = new String(byteAddr,
DIVISION_BYTE_START_INDEX,
byteAddr.length - DIVISION_BYTE_START_INDEX, // 長さ:文字長 - 分割(開始)位置
"MS932").trim();
}
// いわゆる“おまじない”コード(恥)
// result = new String(result.getBytes("MS932"));
} catch(UnsupportedEncodingException ue) {
throw new IllegalArgumentException(ue);
}
return result;
}
}
引数の文字列において、分割される文字列に半角文字(1byte文字)を奇数個含むと、切り口の文字がぶった切られて文字化けする。
例:
"あい うえおかき" → 10 byteで分割 → "あい うえ?"
これをそのままデータベース(oracle)へ insert しようとしたところ、ORA-12899 エラーになった。
データベースの文字コードは Shift_JIS(JA16SJISTILDE)。
カラムに定義された文字長=分割文字長であり、たとえば上記の例であれば:
ORA-12899: 列"DEV"."HOGETABLE"."COLUMN1"の値が大きすぎます(実際: 11、最大: 10)
JDBC(ojdbc14)で insert する直前の値を確認したとき、以下の2パターンが存在した。
前者は末尾の文字が全角の「?」になっている:
1) あい うえ?
2) あい うえ?
1 の場合に ORA-12899 エラーが発生している。
1 は、上記のコードで生成された文字列を用いている。
2 は、上記のコードの おまじない を有効にした場合の文字列を用いている。
1 と 2 の文字列は、(JDBCへ渡す前は)全く同じ文字列に見える:
1) あい うえ? → byte化(MS932):82 a0 82 a2 20 82 a4 82
2) あい うえ? → byte化(MS932):82 a0 82 a2 20 82 a4 82
しかし、String#equals で比較すると「異なる」と判断される。
String#equals は各文字を char で比較する。char にしたときの各文字は 以下のようになる:
1) 'あ' 'い' ' ' 'う' 'え' '・'
2) 'あ' 'い' ' ' 'う' 'え' '?'
そもそもマルチバイトの文字を byte で分割しようとしてるのがアレなのか…
JDBC が余計なことしてくれるのがいけないのか。
別に最後の文字は化けててもいいから、10 byte として突っ込んでくれればいいのに…と思ってしまう。
あと String#equals の挙動も気になる。