package jp.ehobby.util.number; import java.util.HashMap; /** * 数値を扱うユーティリティクラス. * * @author kei-n */ public final class NumberUtilities { /** 16進数の基数(16). */ public static int HEX = 16; /** 10進数の基数(10). */ public static int DEC = 10; /** 8進数の基数(8). */ public static int OCT = 8; /** 2進数の基数(2). */ public static int BIN = 2; //////////////////////////////////////////////////////////////////////////// // // 数値変換用定義 // /** 数値リスト. */ private static final String[] NUMBER_LIST = { "0123456789ABCDEF", //$NON-NLS-1$ "0123456789abcdef", //$NON-NLS-1$ "0123456789ABCDEF", //$NON-NLS-1$ "0123456789abcdef" //$NON-NLS-1$ }; /** 数値MAP. */ private static final HashMap<Character, Integer> NUMBER_MAP = new HashMap<>(); // 数値定義設定 static { for (String numStr : NUMBER_LIST) { for (int num = 0; num < numStr.length(); num++) { char c = numStr.charAt(num); NUMBER_MAP.put(Character.valueOf(c), Integer.valueOf(num)); } } } /** * 文字列表現の値を long 型に変換します. * 数値に変換できない場合、NumberFormatException を throw する. * * @param strNum 文字列表現の値 * @return 値 */ public static long longValue(final String strNum) { NumberAnalyzer analyzer = new NumberAnalyzer(strNum); return analyzer.getValue(); } /** * 指定された文字表現の数値を数値に変換します. * 数値に変換できない場合 -1 を返します. * * @param c 文字表現数値 * @return 数値 */ public static int charToNumber(final char c) { Integer val = NUMBER_MAP.get(Character.valueOf(c)); if (val != null) { return val.intValue(); } return -1; } } /** * 文字列形式の数値解析器. * * @author kei-n */ class NumberAnalyzer { /** 空白文字列. */ private static final String SPACE_STRINGS = " \t_,_, "; //$NON-NLS-1$ /** 数値の末尾に付く付加情報文字. */ private static final String ADDINFO_STRINGS = "ulULhHulULhH"; //$NON-NLS-1$ /** 解析する文字列. */ private final String numStr; /** 解析位置. */ private int pos; /** 符号. */ private int neg; /** 進数. */ private int radix; /** * 数値解析コンストラクタ. * * @param number 解析する文字列形式の数値 */ NumberAnalyzer(final String number) { this.pos = 0; this.radix = NumberUtilities.DEC; this.numStr = trimAll(number); if (this.numStr.length() == 0) { // 空文字、または空白文字だけで構成されている throw new NumberFormatException("\"" + number + "\""); //$NON-NLS-1$//$NON-NLS-2$ } } /** * 解析後の値を返します. * * @return 値 */ public long getValue() { // 符号解析 analysisNegative(); // 進数解析 analysisAdic(); // 数値解析 long value = analysisNumber(); // 符号を付与 value *= this.neg; return value; } /** * 符号解析. * * 負の符号の場合、this.neg を -1 に設定し、this.posを1進めます. * 正の符号の場合、this.neg を 1 に設定し、this.posを1進めます. * 符号なしの場合、this.neg を 1 に設定します. */ private void analysisNegative() { char c = this.numStr.charAt(this.pos); if ((c == '-') || (c == '-') || (c == '‐')) { this.neg = -1; this.pos++; } else if ((c == '+') || (c == '+')) { this.neg = 1; this.pos++; } else { this.neg = 1; } } /** * 進数解析. */ private void analysisAdic() { if ((this.pos + 1) >= this.numStr.length()) { // 残り1文字 => 進数をあらわすもの無しなので10進数とする this.radix = NumberUtilities.DEC; return; } analysisAdicHead(); // 先頭文字を確認し、進数を判定 analysisAdicTail(); // 末尾文字を確認し、進数を判定 } /** * 指定された先頭2文字より、進数を判定します. * 進数判定後、適宜次の読み取り位置を勧めます. */ private void analysisAdicHead() { char c = this.numStr.charAt(this.pos); if ((c != '0') && (c != '0')) { // 先頭0以外の場合は、10進数 this.radix = NumberUtilities.DEC; return; } this.pos++; c = this.numStr.charAt(this.pos); switch (c) { case 'x': case 'X': case 'x': case 'X': // 16進数 this.radix = NumberUtilities.HEX; this.pos++; break; case 'b': case 'B': case 'b': case 'B': // 2進数 this.radix = NumberUtilities.BIN; this.pos++; break; default: // 8進数 this.radix = NumberUtilities.OCT; } } /** * 末尾の文字より、進数を判定します. */ private void analysisAdicTail() { // 最後の文字が h, H か否かを確認 char c = this.numStr.charAt(this.numStr.length() - 1); if ((c == 'h') || (c == 'H') || (c == 'h') || (c == 'H')) { if (this.radix == NumberUtilities.BIN) { // 0B000001 H といった場合、Bは数値とみなす必要がある. this.pos--; } // 末尾に h がついているので 16進数 this.radix = NumberUtilities.HEX; } } /** * 数値を解析し、long型で返します. */ private long analysisNumber() { long value = 0; for (;this.pos < this.numStr.length(); this.pos++) { char c = this.numStr.charAt(this.pos); int tmpVal = NumberUtilities.charToNumber(c); if ((tmpVal < 0) || (this.radix <= tmpVal)) { if (ADDINFO_STRINGS.indexOf(c) >= 0) { break; } throw new NumberFormatException("invalid character '" + c + "'"); //$NON-NLS-1$//$NON-NLS-2$ } value *= this.radix; value += tmpVal; } return value; } //////////////////////////////////////////////////////////////////////////// // // 以下、文字操作などのユーティリティメソッド // /** * 指定された文字列より、空白文字、アンダーバー、カンマなどを * 取り除いた文字列を返します. * @param str 文字列 * @return 空白文字、アンダーバー、カンマを取り除いた文字列 */ private static String trimAll(final String str) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (SPACE_STRINGS.indexOf(c) < 0) { sb.append(c); } } return sb.toString(); } }