Edited at

getBytes("MS932") での文字列分割とDBへのinsertではまった話

More than 5 years have passed since last update.

JDK 1.5.0_12, ojdbc14 での話。


DevideString.java

/** 分割開始インデックス */

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 の挙動も気になる。