15.

Java で文字列が数字か判定する方法完全ガイド — 4 つの実装と性能比較

編集
この記事の要点
  • 最も簡潔なのは str.matches("\d+") だが正規表現コンパイルが毎回走るため高速ではない
  • commons-lang3 の StringUtils.isNumeric() は null/空文字を false で安全に処理。char ループで高速
  • 小数や符号付きを許可するなら NumberUtils.isCreatable() または try-catch で Integer.parseInt
  • 自前実装は Character.isDigit() ループが最速だが Unicode 数字 (全角・アラビア文字数字等) も true になる点に注意
  • パフォーマンス順: char ループ > StringUtils.isNumeric > try-catch >> String.matches

結論: ケース別おすすめ実装

要件おすすめ実装
半角数字のみ判定StringUtils.isNumeric(s) (commons-lang3)
負数・小数も許可NumberUtils.isCreatable(s)
整数化して使うtry-catch で Integer.parseInt(s)
ライブラリ無しで実装Character.isDigit() ループまたは正規表現
大規模数値new BigDecimal(s) を try-catch

方法 1: String.matches() で正規表現

public static boolean isDigitOnly(String s) {
    return s != null && s.matches("\\d+");
}

// 符号付き整数
public static boolean isInteger(String s) {
    return s != null && s.matches("-?\\d+");
}

// 小数
public static boolean isDecimal(String s) {
    return s != null && s.matches("-?\\d+(\\.\\d+)?");
}

欠点: 呼ぶたびに Pattern.compile() が走る。ループ内で頻繁に呼ぶと遅い。改善するなら Pattern を static final にキャッシュ:

private static final Pattern DIGITS = Pattern.compile("\\d+");

public static boolean isDigitOnly(String s) {
    return s != null && DIGITS.matcher(s).matches();
}

方法 2: StringUtils.isNumeric (commons-lang3)

import org.apache.commons.lang3.StringUtils;

StringUtils.isNumeric(null);     // false
StringUtils.isNumeric("");       // false (Lang 3.5+ は false)
StringUtils.isNumeric("  ");     // false
StringUtils.isNumeric("123");    // true
StringUtils.isNumeric("12.3");   // false (ピリオド NG)
StringUtils.isNumeric("-123");   // false (符号 NG)
StringUtils.isNumeric("123"); // false (全角は NG、Character.isDigit() とは違う)
StringUtils.isNumeric("12 3");   // false

内部実装は Character.isDigit() を文字ごとに呼ぶだけのシンプルなループ。正規表現より高速です。

方法 3: NumberUtils.isCreatable (commons-lang3)

「Java の数値リテラルとして解釈可能か」を判定。符号・小数・16 進・指数表記・型サフィックスまで対応:

import org.apache.commons.lang3.math.NumberUtils;

NumberUtils.isCreatable("123");      // true
NumberUtils.isCreatable("-123");     // true
NumberUtils.isCreatable("1.23");     // true
NumberUtils.isCreatable("1.23e5");   // true
NumberUtils.isCreatable("0x1A");     // true (16 進)
NumberUtils.isCreatable("123L");     // true (long サフィックス)
NumberUtils.isCreatable("1.23f");    // true (float サフィックス)
NumberUtils.isCreatable("abc");      // false
NumberUtils.isCreatable("");         // false
NumberUtils.isCreatable(null);       // false

旧名 isNumber() は deprecated。新規コードは isCreatable を使う

方法 4: try-catch で Integer.parseInt

判定後に数値として使うなら、try-catch で直接パースする方がコードが綺麗:

public static Integer tryParseInt(String s) {
    if (s == null) return null;
    try {
        return Integer.parseInt(s);
    } catch (NumberFormatException e) {
        return null;
    }
}

// Java 8+ Optional 版
public static OptionalInt parseInt(String s) {
    if (s == null) return OptionalInt.empty();
    try {
        return OptionalInt.of(Integer.parseInt(s));
    } catch (NumberFormatException e) {
        return OptionalInt.empty();
    }
}

欠点: 例外コストが大きい。「数字でない確率が高い」入力では遅くなる。例外スローのスタックトレース生成が重い。

方法 5: Character.isDigit() で自前ループ

public static boolean isDigitOnly(String s) {
    if (s == null || s.isEmpty()) return false;
    for (int i = 0; i < s.length(); i++) {
        if (!Character.isDigit(s.charAt(i))) return false;
    }
    return true;
}

最速。ただし Character.isDigit('1') (全角 1) や Character.isDigit('٥') (アラビア数字 5) は true を返すため、ASCII 限定なら以下:

public static boolean isAsciiDigitOnly(String s) {
    if (s == null || s.isEmpty()) return false;
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c < '0' || c > '9') return false;
    }
    return true;
}

方法 6: BigDecimal で精度を保つ

金額計算など precision が重要な場合は BigDecimal を使う:

public static boolean isDecimalSafe(String s) {
    if (s == null || s.isEmpty()) return false;
    try {
        new BigDecimal(s);
        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}

// double は浮動小数点誤差があるため業務系では避ける
// new BigDecimal("123.456") → 正確
// Double.parseDouble("123.456") → 123.45600000000000307...

性能比較

1 万回呼び出しのおおよその目安 (環境依存):

実装相対速度備考
char ループ (ASCII 比較)★★★★★ (最速)分岐 1 つだけ
Character.isDigit() ループ★★★★☆Unicode 判定の分やや遅い
StringUtils.isNumeric()★★★★☆上と同等
Pattern キャッシュ + matches★★★☆☆正規表現エンジンのオーバーヘッド
try-catch Integer.parseInt (成功時)★★★☆☆例外なしなら早い
try-catch Integer.parseInt (失敗時)★☆☆☆☆例外スローで激遅
String.matches() (キャッシュなし)★★☆☆☆毎回 compile

null / 空文字の扱い

仕様によって null や空文字 ("") を「数字 OK」にするか「NG」にするか分岐します。多くの場合 NG が安全:

// 安全側 (null/空 → false)
public static boolean isDigit(String s) {
    return s != null && !s.isEmpty() && s.chars().allMatch(Character::isDigit);
}

// Java 11+ String.isBlank() で空白文字も除外
public static boolean isDigitStrict(String s) {
    return s != null && !s.isBlank() && s.chars().allMatch(Character::isDigit);
}

負数・小数を許容する判定の細部

// 整数 (符号付き)
"-?\\d+"

// 小数 (符号付き、小数部任意)
"-?\\d+(\\.\\d+)?"

// 指数表記対応 (1.23e5 / -1.5E-3)
"-?\\d+(\\.\\d+)?([eE][+-]?\\d+)?"

// 千の区切り対応 (1,234,567)
"-?\\d{1,3}(,\\d{3})*(\\.\\d+)?"

FAQ

Q: 全角数字 (123) を許容したい
A: Character.isDigit() は全角もアラビア数字も true。完全に全角を許可するなら Normalizer.normalize(s, Form.NFKC) で半角化してから判定。

Q: scientific notation (1.5e+10) を判定したい
A: NumberUtils.isCreatable() がそのまま使える。または Double.parseDouble() を try-catch。

Q: Long の範囲を超える数字を判定したい
A: new BigInteger(s) を try-catch。あるいは StringUtils.isNumeric() + 桁数チェック。

Q: 先頭ゼロ (007) はどう扱う?
A: StringUtils.isNumeric("007") は true。Integer.parseInt("007") は 7 として成功。8 進数解釈はしない。

関連項目

  • commons-lang3 StringUtils / NumberUtils
  • Integer.parseInt / Long.parseLong / BigDecimal
  • Java Regex Pattern キャッシュ
  • Normalizer (全角半角統一)
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. プラットホーム
  2. 環境構築
  3. 文法
  4. API
  5. Servlet(サーブレット)
  6. JSP
  7. Applet(アプレット)
  8. デザインパターン
  9. フレームワーク
  10. ライブラリ
  11. Androidアプリケーション
  12. Project Jigsaw
  13. エラー一覧
  14. 日付の加算、減算
  15. 文字列の数字チェック
  16. 改行コードの削除
  17. 先頭と末端の文字の削除
  18. warファイルの中身を確認する方法
  19. nullもしくは空文字の判定
  20. beanの中身を確認する方法
  21. org.apache.log4j.Logger のログ出力で printStackTrace() のエラー内容を出力する方法
  22. Javaのバージョン確認方法