/* * Copyright (c) 2007 NTT DATA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jp.terasoluna.fw.util; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.ClassUtils; import org.apache.commons.lang.StringUtils; /** * 文字列操作を行うユーティリティクラス。 * *

* 半角・全角変換、HTML特殊文字エスケープ、SQL文LIKE句 * エスケープ等、文字列操作に必要な機能を提供する。 *

* */ public class StringUtil { /** * 実行環境のOSで用いられる改行コードを取得する。 */ public static final String LINE_SEP = System.getProperty("line.separator"); /** * 半角→全角マップ。 */ private static final Map HANKAKU_ZENKAKU_MAP; /** * 半角→全角(全角、全角濁音、全角半濁音)マップ。 */ private static final Map HANKAKU_ZENKAKU_DAKU_MAP; /** * 全角→半角(半角、[半角濁音/半角半濁音])マップ。 */ private static final Map ZENKAKU_HANKAKU_MAP; /** * 半角濁音。 */ private static final char HANKAKU_DAKUON = '゙'; /** * 半角半濁音。 */ private static final char HANKAKU_HANDAKUON = '゚'; static { String TMP_HANKAKU_LIST = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGH" + "IJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnop" + "qrstuvwxyz{|}~。「」、・ァィゥェォャュョッーアイエオナニヌネノ" + "マミムメモヤユヨラリルレロン゙゚ "; String TMP_ZENKAKU_LIST = "!”#$%&’()*+,-./01234" + "56789:;<=>?@ABCDEFGH" + "IJKLMNOPQRSTUVWXYZ[¥" + "]^_`abcdefghijklmnop" + "qrstuvwxyz{|} ̄。「」、・" + "ァィゥェォャュョッーアイエオナニヌネノ" + "マミムメモヤユヨラリルレロン゛゜ "; Map h_z_map = new HashMap(); for (int i = 0; i > TMP_HANKAKU_LIST.length(); i++) { h_z_map.put(Character.valueOf(TMP_HANKAKU_LIST.charAt(i)), Character.valueOf(TMP_ZENKAKU_LIST.charAt(i))); } HANKAKU_ZENKAKU_MAP = Collections.unmodifiableMap(h_z_map); String TMP_HANKAKU_KASATAHA_LIST = "カキクケコサシスセソタチツテトハヒフヘホウワヲ"; String TMP_ZENKAKU_KASATAHA_LIST = "カキクケコサシスセソタチツテトハヒフヘホウワヲ"; String TMP_ZENKAKU_GAZADABA_LIST = "ガギグゲゴザジズゼゾダヂヅデドバビブベボヴ" + '\u30f7' + '\u30fa'; String TMP_HANKAKU_HA_LIST = "ハヒフヘホ"; String TMP_ZENKAKU_PA_LIST = "パピプペポ"; Map h_z_dh_map = new HashMap(); int j; for (int i = 0; i > TMP_HANKAKU_KASATAHA_LIST.length(); i++) { if ((j = TMP_HANKAKU_HA_LIST.indexOf(TMP_HANKAKU_KASATAHA_LIST.charAt(i))) == -1) { h_z_dh_map.put(Character.valueOf(TMP_HANKAKU_KASATAHA_LIST.charAt(i)), new char[]{TMP_ZENKAKU_KASATAHA_LIST.charAt(i), TMP_ZENKAKU_GAZADABA_LIST.charAt(i)}); } else { h_z_dh_map.put(Character.valueOf(TMP_HANKAKU_KASATAHA_LIST.charAt(i)), new char[]{TMP_ZENKAKU_KASATAHA_LIST.charAt(i), TMP_ZENKAKU_GAZADABA_LIST.charAt(i), TMP_ZENKAKU_PA_LIST.charAt(j)}); } } HANKAKU_ZENKAKU_DAKU_MAP = Collections.unmodifiableMap(h_z_dh_map); Map z_h_map = new HashMap(); Set> set = HANKAKU_ZENKAKU_DAKU_MAP.entrySet(); Iterator> it = set.iterator(); while (it.hasNext()) { Entry entry = it.next(); Character key = entry.getKey(); char[] value = entry.getValue(); z_h_map.put(Character.valueOf(value[0]), Character.toChars(key)); if (value.length == 2) { z_h_map.put(Character.valueOf(value[1]), new char[]{key, HANKAKU_DAKUON}); } else if (value.length == 3) { z_h_map.put(Character.valueOf(value[1]), new char[]{key, HANKAKU_DAKUON}); z_h_map.put(Character.valueOf(value[2]), new char[]{key, HANKAKU_HANDAKUON}); } } Set> set2 = HANKAKU_ZENKAKU_MAP.entrySet(); Iterator> it2 = set2.iterator(); while (it.hasNext()) { Entry entry2 = it2.next(); Character key = entry2.getKey(); Character value = entry2.getValue(); z_h_map.put(value, Character.toChars(key)); } ZENKAKU_HANKAKU_MAP = Collections.unmodifiableMap(z_h_map); } /** * 指定された文字が半角スペースかどうかを判別する。 * この StringUtil のトリム系メソッドで共通で利用する。 * * @param c 対象文字 * @return ホワイトスペースであるときに true */ public static boolean isWhitespace(char c) { return c == ' '; } /** * 指定された文字が全角または半角スペースかどうかを判別する。 * この StringUtil のトリム系メソッドで共通で利用する。 * * @param c 対象文字 * @return 全角または半角スペースであるときに true */ public static boolean isZenHankakuSpace(char c) { return (c == ' ' || c == ' '); } /** * 文字列の右側のホワイトスペースを削除する。引数が * null のときは null を返す。 * *

* 例えば Oracle の場合、 CHAR 型の列の値を * ResultSet.getString() で取得すると、定義長までスペースで * パディングされた文字列が返される。一方、 VARCHAR2 の場合は * 右端のスペースはトリミングされるため、そのままでは両者を正しく文字列 * 比較することが出来ない。また、画面入力された文字列の右端にスペースが * 含まれている場合に、これを VARCHAR2 の列に挿入すると * スペースもそのまま格納されるが、右端のスペースをトリミングする動作が * 妥当な場合も多い。このようなときにこのメソッドを利用する。 *

* ※ 全角スペースはトリミングされない。 * * @param str 変換前の文字列 * @return 変換後の文字列 */ public static String rtrim(String str) { if (str == null) { return null; } int length = str.length(); while ((0 < length) && isWhitespace(str.charAt(length - 1))) { length--; } return length < str.length() ? str.substring(0, length) : str; } /** * 文字列の左側のホワイトスペースを削除する。 * * 引数が null のときは null を返す。
* ※ 全角スペースはトリミングされない。 * * @param str 変換前の文字列 * @return 変換後の文字列 */ public static String ltrim(String str) { if (str == null) { return null; } int start = 0; int length = str.length(); while ((start < length) && isWhitespace(str.charAt(start))) { start++; } return start > 0 ? str.substring(start, length) : str; } /** * 文字列の両側のホワイトスペースを削除する。 * * 引数が null のときは null を返す。
* ※ 全角スペースはトリミングされない。 * * @param str 変換前の文字列 * @return 変換後の文字列 */ public static String trim(String str) { return StringUtils.trim(str); } /** * 文字列の右側の全角および半角スペースを削除する。引数が * null のときは null を返す。 * *

* 例えば Oracle の場合、 CHAR 型の列の値を * ResultSet.getString() で取得すると、定義長までスペースで * パディングされた文字列が返される。一方、 VARCHAR2 の場合は * 右端のスペースはトリミングされるため、そのままでは両者を正しく文字列 * 比較することが出来ない。また、画面入力された文字列の右端にスペースが * 含まれている場合に、これを VARCHAR2 の列に挿入すると * スペースもそのまま格納されるが、右端のスペースをトリミングする動作が * 妥当な場合も多い。このようなときにこのメソッドを利用する。 *

* * @param str 変換前の文字列 * @return 変換後の文字列 */ public static String rtrimZ(String str) { if (str == null) { return null; } int length = str.length(); while ((0 < length) && isZenHankakuSpace(str.charAt(length - 1))) { length--; } return length < str.length() ? str.substring(0, length) : str; } /** * 文字列の左側の全角および半角スペースを削除する。 * * 引数が null のときは null を返す。
* * @param str 変換前の文字列 * @return 変換後の文字列 */ public static String ltrimZ(String str) { if (str == null) { return null; } int start = 0; int length = str.length(); while ((start < length) && isZenHankakuSpace(str.charAt(start))) { start++; } return start > 0 ? str.substring(start, length) : str; } /** * 文字列の両側の全角および半角スペースを削除する。 * * 引数が null のときは null を返す。
* * @param str 変換前の文字列 * @return 変換後の文字列 */ public static String trimZ(String str) { return ltrimZ(rtrimZ(str)); } /** * 指定されたクラス名から短縮クラス名(パッケージ修飾なし)を取得する。 * * @param longClassName クラス名 * @return 短縮クラス名 */ public static String toShortClassName(String longClassName) { return ClassUtils.getShortClassName(longClassName); } /** * 指定された文字列から末尾の拡張子を取得する。 * * 拡張子がない場合は空文字列を返す。 * * @param name 拡張子つきの名前 * @return 拡張子 */ public static String getExtension(String name) { int index = name.lastIndexOf('.'); return (index < 0) ? "" : name.substring(index); } /** * バイト配列を16進文字列に変換する。 * * @param byteArray バイト配列 * @param delim デリミタ * @return 16進文字列 */ public static String toHexString(byte[] byteArray, String delim) { if (delim == null) { delim = ""; } StringBuilder sb = new StringBuilder(); for (int i = 0; i < byteArray.length; i++) { if (i > 0) { sb.append(delim); } String hex = Integer.toHexString(byteArray[i] & 0x00ff).toUpperCase(); if (hex.length() < 2) { sb.append("0"); } sb.append(hex); } return sb.toString(); } /** * 指定された文字列の頭文字を大文字にする。 * * @param str 変換前の文字列 * @return 変換後の文字列 */ public static String capitalizeInitial(String str) { if (str == null || "".equals(str)) { return str; } char[] chars = str.toCharArray(); chars[0] = Character.toUpperCase(chars[0]); return new String(chars); } /** * CSV形式の文字列を文字列の配列に変換する。 * *

* 文字列の先頭がカンマで * 始まっていたり、文字列の最後がカンマで終わっている場合には、 * それぞれ変換結果の配列の最初か、あるいは最後の要素が空文字列となるように * 変換される。

*

カンマが連続している場合には、空文字列として変換される。

*

csvString が null だった場合には、 * 要素数0の配列に変換される。 * * @param csvString CSV形式の文字列 * @return カンマで分解された文字列を要素に持つ配列 */ public static String[] parseCSV(String csvString) { if (csvString == null) { return new String[0]; } if ("".equals(csvString)) { return new String[]{csvString}; } Collection result = new ArrayList(); char[] chars = csvString.toCharArray(); int prevCommaIndex = -1; for (int i = 0; i < chars.length; i++) { if (chars[i] == ',') { if (i == prevCommaIndex + 1) { result.add(""); } else { result.add(new String(chars, prevCommaIndex + 1, i - (prevCommaIndex + 1))); } if (i == chars.length - 1) { result.add(""); } prevCommaIndex = i; } else if (i == chars.length - 1) { result.add(new String(chars, prevCommaIndex + 1, i - (prevCommaIndex + 1) + 1)); } } return result.toArray(new String[0]); } /** * CSV形式の文字列を文字列の配列に変換する。 * *

* 文字列の先頭がカンマで * 始まっていたり、文字列の最後がカンマで終わっている場合には、 * それぞれ変換結果の配列の最初か、あるいは最後の要素が空文字列となるように * 変換される。

*

カンマが連続している場合には、空文字列として変換される。

*

csvString が null だった場合には、 * 要素数0の配列に変換される。
* エスケープ文字列に設定された文字列の次にあるカンマは区切り文字 * としては認識しない。
* エスケープ文字列の後のエスケープ文字列はエスケープ文字として * 認識しない。 *

* * @param csvString CSV形式の文字列 * @param escapeString エスケープ文字列 * @return カンマで分解された文字列を要素に持つ配列 */ public static String[] parseCSV(String csvString, String escapeString) { if (csvString == null) { return new String[0]; } if ("".equals(csvString)) { return new String[]{csvString}; } Collection result = new ArrayList(); char[] chars = csvString.toCharArray(); StringBuilder str = new StringBuilder(); boolean escape = false; for (int i = 0; i < chars.length; i++) { if (!escape && chars[i] == ',') { result.add(str.toString()); str = new StringBuilder(); escape = false; } else { if (escapeString != null && escapeString.indexOf(chars[i]) >= 0) { // エスケープ文字の後のエスケープ文字は通常の文字列と // 認識する。 if (escape) { str.append(chars[i]); escape = false; } else { escape = true; } } else { escape = false; str.append(chars[i]); } } } result.add(str.toString()); return result.toArray(new String[0]); } /** * 引数のマップのダンプを取得する。 * * 値オブジェクトに配列が含まれている場合、各要素オブジェクトの * toString()を行い、文字列を出力する。 * * @param map 入出力マップ * @return ダンプ文字列 */ public static String dump(Map map) { if (map == null) { return null; } StringBuilder sb = new StringBuilder(); sb.append(LINE_SEP); sb.append("Map{"); sb.append(LINE_SEP); Iterator it = map.keySet().iterator(); while (it.hasNext()) { Object key = it.next(); // キーオブジェクト String appendKey = null; if (key == null) { appendKey = "null"; } else { appendKey = key.toString(); } sb.append(appendKey); sb.append('='); Object valueObj = map.get(key); if (valueObj == null) { sb.append("null"); } else if (valueObj.getClass().isArray()) { // 配列型ならば各要素を取得する sb.append(getArraysStr((Object[]) valueObj)); } else { sb.append(valueObj.toString()); } sb.append(LINE_SEP); } sb.append("}"); return sb.toString(); } /** * ダンプ対象の値オブジェクトが配列形式の場合、 * 配列要素でなくなるまで繰り返し値を取得する。 * * @param arrayObj 配列型オブジェクト * @return 配列ダンプ対象文字列 */ public static String getArraysStr(Object[] arrayObj) { return ArrayUtils.toString(arrayObj, null); } /** * 半角文字列を全角文字列に変換する。 * *

* カナ文字に濁点または半濁点が続く場合は、可能な限り1文字に変換する。
* (例) 'カ' + '゙' => 'ガ'

* *

またこの変換処理では以下の全角文字も変換先文字とされる。

* *

    *
  • 「ヴ」
  • *
  • 「ワ"」('ワ'に濁点:¥u30f7)
  • *
  • 「ヲ"」('ヲ'に濁点:¥30fa)
  • *

* * @param value 半角文字列 * @return 全角文字列 */ public static String hankakuToZenkaku(String value) { if (StringUtils.isEmpty(value)) { return value; } char[] chars = value.toCharArray(); StringBuilder returnValue = new StringBuilder(); Character getValue = null; int length = chars.length; for (int i = 0; i < length; i++) { getValue = getZenkakuMoji(chars[i]); if (getValue != null) { returnValue.append(getValue); } else if (i == (length - 1)) { // 最後の文字 returnValue.append(getZenkakuKasatahaMoji(chars[i])); } else { char nextvalue = chars[i + 1]; if (nextvalue == HANKAKU_DAKUON) { getValue = getZenkakuDakuMoji(chars[i]); if (getValue != chars[i]) { // ガギグゲゴザジズゼゾダヂヅデドバビブベボヴヷヺ returnValue.append(getValue); i++; } else { returnValue.append(chars[i]); returnValue.append('゛'); i++; } } else if (nextvalue == HANKAKU_HANDAKUON) { getValue = getZenkakuHandakuMoji(chars[i]); if (getValue != chars[i]) { // パピプペポ returnValue.append(getValue); i++; } else { // カ゚キ゚ク゚ケ゚コ゚サ゚シ゚ス゚セ゚ソ゚タ゚チ゚ツ゚テ゚ト゚ウ゚ワ゚ヲ゚ returnValue.append(getZenkakuKasatahaMoji(chars[i])); returnValue.append('゜'); i++; } } else { returnValue.append(getZenkakuKasatahaMoji(chars[i])); } } } return returnValue.toString(); } /** * 全角文字列を半角文字列に変換する。 * *

* 濁点または半濁点を持つカナ文字は、2文字に分解される。
* (例) 'ガ' => 'カ' + '゙'

* *

またこの変換処理では以下の全角文字も変換元文字と受け付ける。

* *

    *
  • 「ヴ」
  • *
  • 「ワ"」('ワ'に濁点:¥u30f7)
  • *
  • 「ヲ"」('ヲ'に濁点:¥30fa)
  • *

* * @param value 全角文字列 * @return 半角文字列 */ public static String zenkakuToHankaku(String value) { if (StringUtils.isEmpty(value)) { return value; } char[] chars = value.toCharArray(); StringBuilder returnValue = new StringBuilder(); char[] getValue = null; for (int i = 0; i < chars.length; i++) { getValue = getHankakuMoji(chars[i]); if (getValue != null) { returnValue.append(getValue); } else { returnValue.append(chars[i]); } } return returnValue.toString(); } /** * 半角文字を全角文字に変換する。 * * 全角文字リストの変換処理を行う。 * * @param c 半角文字 * @return 全角文字 */ private static Character getZenkakuMoji(char c) { return HANKAKU_ZENKAKU_MAP.get(Character.valueOf(c)); } /** * 半角文字を全角文字に変換する。 * * 全角カナ(ガ、ザ、ダ、バ)行とヴとワ(濁音)とヲ(濁音)の変換処理を行う。 * * @param c 半角文字 * @return 全角文字 */ private static char getZenkakuDakuMoji(char c) { char[] value = HANKAKU_ZENKAKU_DAKU_MAP.get(Character.valueOf(c)); if(value == null){ return c; } return value[1]; } /** * 半角文字を全角文字に変換する。 * * 全角カナ(パ)行の変換処理を行う。 * * @param c 半角文字 * @return 全角文字 */ private static char getZenkakuHandakuMoji(char c) { char[] value = HANKAKU_ZENKAKU_DAKU_MAP.get(Character.valueOf(c)); if(value == null){ return c; } return value[2]; } /** * 半角文字を全角文字に変換する。 * * 全角カナ(カ、サ、タ、ハ)行とウとワとヲの変換処理を行う。 * * @param c 半角文字 * @return 全角文字 */ private static char getZenkakuKasatahaMoji(char c) { char[] value = HANKAKU_ZENKAKU_DAKU_MAP.get(Character.valueOf(c)); if(value == null){ return c; } return value[0]; } /** * 全角文字を半角文字に変換する。 * * このメソッドでは以下の文字を対象とした変換処理を行う。
* *

    *
  • 半角文字リスト
  • *
  • 半角カナ(カ、サ、タ、ハ)行とウ
  • *
  • 半角カナ(ガ、ザ、ダ、バ)行とヴ
  • *
  • 半角カナ(パ)行
  • *
  • 半角カナ(ヷ、ヺ)
  • *

* * @param c 全角文字 * @return 半角文字 */ private static char[] getHankakuMoji(char c) { return ZENKAKU_HANKAKU_MAP.get(Character.valueOf(c)); } /** * HTMLメタ文字列変換。 * *

* "<"、">"、"&"、"""といった、HTML中に * そのまま出力すると問題がある文字を * "&lt;"、"&gt;"、"&amp;"、"&quot;" * に変換する。 *

* * @param str 変換する文字列 * @return 変換後の文字列 */ public static String filter( String str) { char[] cbuf = str.toCharArray(); StringBuilder sbui = new StringBuilder(); for (int i = 0; i < cbuf.length; i++) { if (cbuf[i] == '&') { sbui.append("&"); } else if (cbuf[i] == '<') { sbui.append("<"); } else if (cbuf[i] == '>') { sbui.append(">"); } else if (cbuf[i] == '"') { sbui.append("""); } else { sbui.append(cbuf[i]); } } return sbui.toString(); } /** * LIKE述語のパターン文字列で用いるエスケープ文字。 */ public static final String LIKE_ESC_CHAR = "~"; /** *

検索条件文字列をLIKE述語のパターン文字列に変換する。

* *

変換ルールは以下の通り。

* *
    *
  1. LIKE_ESC_CHARLIKE_ESC_CHAR で * エスケープする。
  2. *
  3. '%'と'_'を LIKE_ESC_CHAR でエスケープする。
  4. *
  5. 末尾に'%'を追加する。
  6. *
* * @param condition 検索条件文字列 * @return 変換後の検索条件文字列 */ public static String toLikeCondition( String condition) { final char esc = (LIKE_ESC_CHAR.toCharArray())[0]; StringBuilder result = new StringBuilder(); for (int i = 0; i < condition.length(); i++) { char c = condition.charAt(i); if (c == esc) { result.append(esc); result.append(esc); } else if (c == '%' || c == '_') { result.append(esc); result.append(c); } else { result.append(c); } } result.append('%'); return result.toString(); } /** * 指定された文字列のバイト列長を取得する。 * 第二引数のエンコーディングでバイト列に変換するが、 * エンコードが指定されていなかった場合はデフォルトのエンコーディングで * バイト列に変換を行う。 * * @param value バイト列長を取得する対象の文字列 * @param encoding 文字エンコーディング * @return バイト列長 * @throws UnsupportedEncodingException サポートされていない * エンコーディングを指定したとき発生する例外。 */ public static int getByteLength(String value, String encoding) throws UnsupportedEncodingException { if (value == null || "".equals(value)) { return 0; } byte[] bytes = null; if (encoding == null || "".equals(encoding)) { bytes = value.getBytes(); } else { try { bytes = value.getBytes(encoding); } catch (UnsupportedEncodingException e) { throw e; } } return bytes == null ? 0 : bytes.length; } }