Help us understand the problem. What is going on with this article?

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

tmd45
昔は Java & Struts1.x、いまは Ruby & Rails、最近はマネジメント寄り。Eclipse → Vim → VS Code 使い。好きな言葉は『短気は損気』。ブログに書くことのほうが多いです
https://blog.tmd45.jp/
feedforce
『「働く」を豊かにする。』というミッションを掲げ、企業向けネットサービスを開発・提供しています。
https://www.feedforce.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away