Java库以不抛出异常的方式检查字符串是否包含数字

8
我希望能够提供一种方法,如果传递的字符串是一个有效的数字(例如“123.55e-9”,“-333,556”),则该方法将返回布尔值。我不想只是这样做:
public boolean isANumber(String s) {
    try { 
        BigDecimal a = new BigDecimal(s); 
        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}

显然,该函数应使用状态机(DFA)解析字符串,以确保无效示例不会欺骗它(例如“-21,22.22.2”,“33-2”)。你知道是否存在这样的库吗?我真的不想自己编写它,因为这是一个非常明显的问题,我相信我一定会重新发明轮子。

谢谢,

Nick


为什么你不想使用 BigDecimal 的解析呢?这是最简单的方法了,真的。 - Jorn
因为异常不适用于这种情况。异常是用来处理异常情况的,而不是用来控制程序的。 - Malax
2
我也遇到过这个问题几次,虽然这样利用异常感觉不正确,但它仍然是最简单的方法。它简单易行,经过充分测试并且有效。我不再寻找其他方法了。 - Narayan Raman
1
从理论上讲,我同意这个观点。如果您可以控制BigDecimal中使用的解析方式,那么您可以将其更改为返回一些值以指示解析失败,而不是抛出异常。然而,由于您无法控制它,我不会为已经存在的东西制作自己的实现,仅仅因为BigDecimal的方式“不太好,因为它使用异常”。 - Jorn
-1 这是非常苛刻的。 BigDecimal 会为您处理它。 如果您不喜欢异常,请将其放在 Utils 类中并将其命名为“safeIsNum()”或其他类似名称。 Apache Commnons 还有一些字符串验证检查,也可以做到这种事情。 - Chris Kessel
显示剩余2条评论
5个回答

6

我建议不要重新发明这个方法,而是使用Apache Commons。如果你在使用Spring、Struts或其他常用的Java库,则它们经常包含了Apache Commons。你需要用到commons-lang.jar文件。这里是NumberUtils中你所需要的方法:

isNumber[1]

public static boolean isNumber(java.lang.String str)
Checks whether the String a valid Java number.

Valid numbers include hexadecimal marked with the 0x qualifier, scientific notation and numbers marked with a type qualifier (e.g. 123L).

Null and empty String will return false.

Parameters:
str - the String to check
Returns:
true if the string is a correctly formatted number

isNumber现在已经被弃用了,所以请使用isParsable。 - Phil

3

关于Double.valueOf(String)的精确正则表达式在Javadocs中有详细说明。

To avoid calling this method on an invalid string and having a NumberFormatException be thrown, the regular expression below can be used to screen the input string:

final String Digits     = "(\\p{Digit}+)";
final String HexDigits  = "(\\p{XDigit}+)";
// an exponent is 'e' or 'E' followed by an optionally 
// signed decimal integer.
final String Exp        = "[eE][+-]?"+Digits;
final String fpRegex    =
       ("[\\x00-\\x20]*"+  // Optional leading "whitespace"
        "[+-]?(" + // Optional sign character
        "NaN|" +           // "NaN" string
        "Infinity|" +      // "Infinity" string

        // A decimal floating-point string representing a finite positive
        // number without a leading sign has at most five basic pieces:
        // Digits . Digits ExponentPart FloatTypeSuffix
        // 
        // Since this method allows integer-only strings as input
        // in addition to strings of floating-point literals, the
        // two sub-patterns below are simplifications of the grammar
        // productions from the Java Language Specification, 2nd 
        // edition, section 3.10.2.

        // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
        "((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+

        // . Digits ExponentPart_opt FloatTypeSuffix_opt
        "(\\.("+Digits+")("+Exp+")?)|"+

        // Hexadecimal strings
        "((" +
        // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
        "(0[xX]" + HexDigits + "(\\.)?)|" +

        // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
        "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +

        ")[pP][+-]?" + Digits + "))" +
        "[fFdD]?))" +
        "[\\x00-\\x20]*"); // Optional trailing "whitespace"
       
if (Pattern.matches(fpRegex, myString))
    Double.valueOf(myString); // Will not throw NumberFormatException
else {
    // Perform suitable alternative action
}

3

精确地说,这里有一些关于单元测试的食物:http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html#BigDecimal(java.lang.String)。 - flybywire

2

使用正则表达式可以解决问题。我只熟悉.Net的正则表达式,但所有的正则表达式语言都相似,所以这应该能帮助你入门。我没有测试过它,所以你可能需要在Java正则表达式类中测试一下。

"-?(([0-9]{1,3}(,[0-9{3,3})*)|[0-9]*)(\.[0-9]+(e-?[0-9]*)?)?"

一些正则表达式控制语法:
? - 可选元素
| - 或操作符。如果格式正确,允许数字带或不带逗号。
[ ] - 允许字符集
{ , } - 元素的最小和最大值
* - 任意数量的元素,从0到无穷大
+ - 至少一个元素,从1到无穷大
\ - 转义字符
. - 任何字符(因此需要转义)


实际上,这个表达式将匹配22、22和2.14123415e1。 - Salandur

2

以下是一个基于正则表达式的实用函数(在保持可读性的同时无法将""检查放入正则表达式中):

public class TestRegexp {
    static final String NUM_REGEX=
        "-?((([0-9]{1,3})(,[0-9]{3})*)|[0-9]*)(\\.[0-9]+)?([Ee][0-9]*)?";
    public static boolean isNum(String s) {
            return s!=null && s.length()>0 && s.matches(NUM_REGEX);  
    }
    public static void main(String[]args) {
        String[] values={
                "",
                "0",
                "0.1",
                ".1",
                "-.5E5",
                "-12,524.5E5",
                "-452,456,456,466.5E5",
                "-452,456,456,466E5",
                "22,22,2.14123415e1",
        };
        for (String value : values) {
            System.out.println(value+" is a number: "
            +isNum(value));
        }
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接