end0tknr's kipple - 新web写経開発

http://d.hatena.ne.jp/end0tknr/ から移転します

java.lang.Stringのtrim()の全角空白対応を考える

java.lang.Stringのtrim()が全角空白を削除しないことは有名(当然)なことですが...

trim()の削除対象は\u0020以下の文字コードの「\u0020以下」って何?

docs.oracle.com にある java.lang.Stringのドキュメントには、trim()の削除対象を\u0020以下のコードと記載されており、当然、java.lang.Stringのsrcもそのように実装されています。でも「\u0020以下」って?

以下は http://docs.oracle.com/javase/jp/6/api/java/lang/String.html#trim%28%29 の抜粋

public String trim()
文字列のコピーを返します。先頭と最後の空白は省略されます。
この String オブジェクトが空の文字列を表す場合、あるいはこの String オブジェクトによって表される文字列の最初と最後の文字のコードが '\u0020' (スペース文字) より大きい場合は、この String オブジェクトへの参照が返されます。

文字列内に '\u0020' より大きいコードの文字がない場合は、空の文字列を表す新しい String オブジェクトが生成されて返されます。

たとえば、k が文字列内の最初の文字のインデックスであり、「\u0020」より大きいコード値を持ち、m が文字列内の最後の文字のインデックスであり、「\u0020」より大きいコード値を持つ場合は、インデックス k にある文字で始まり、インデックス m にある文字で終わる、この文字列の部分文字列を表す新しい String オブジェクトが生成されます。つまり、これは this.substring(k, m+1) の結果と同じです。

このメソッドは文字列の先頭と最後から (上記で定義された) 空白を切り取るために使用できます。

戻り値:
この文字列の先頭と最後の空白を削除したコピー、またはこの文字列 (先頭と最後に空白が存在しない場合)

java.lang.Stringのsrc

public String trim() {
    int len = value.length;
    int st = 0;
    char[] val = value;    /* avoid getfield opcode */

    while ((st < len) && (val[st] <= ' ')) {
        st++;
    }
    while ((st < len) && (val[len - 1] <= ' ')) {
        len--;
    }
    return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}

unicode.orgにある文字コード( Ux0020以下とUx3000(全角空白) )を確認

すると、次の通り定義されている為、trim()の削除対象が\u0020以下の文字コードであることが理解できます。
http://www.unicode.org/charts/PDF/U0000.pdf
http://www.unicode.org/charts/PDF/U3000.pdf

16進 - 16進 -
0000 NUL (NULL文字) 0001 SOH (ヘッダ開始)
0002 STX (テキスト開始) 0003 ETX (テキスト終了)
0004 EOT (転送終了) 0005 ENQ (照会)
0006 ACK (受信OK) 0007 BEL (警告)
0008 BS (後退) 0009 HT (水平タブ)
000A LF (改行) 000B VT (垂直タブ)
000C FF (改頁) 000D CR (復帰)
000E SO (Shift Out) 000F SI (Shift In)
0010 DLE (データリンクエスケープ) 0011 DC1 (装置制御1)
0012 DC2 (装置制御2) 0013 DC3 (装置制御3)
0014 DC4 (装置制44) 0015 NAK (受信失敗)
0016 SYN (同期) 0017 ETB (転送ブロック終了)
0018 CAN (取消) 0019 EM (メディア終了)
001A SUB (置換) 001B ESC (エスケープ)
001C FS (フォーム区切) 001D GS (グループ区切)
001E RS (レコード区切) 001F US (ユニット区切)
0020 SP (スペース) - -
3000 IDSP (全角空白) - -

そこで、全角空白に対応したtrim()を書くなら...

次のような感じかな?

public String trimSpace(String orgStr) {
    char[] value = orgStr.toCharArray();
    int len = value.length;
    int st = 0;
    char[] val = value;
    
    while ((st < len) && (val[st] <= ' ' || val[st] == ' ')) {
        st++;
    }
    while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == ' ')) {
        len--;
    }
    
    return ((st>0) || (len<value.length)) ? orgStr.substring(st,len):orgStr;
}