为避免NumberFormatException如何检查字符串是否为数字?


在本文中,我们将通过几种方法检查 String 在 Java 中是否为数值型?

使用内置方法
最简单的方法是使用 Java 中的内置方法,例如Double.parseDouble(String str)。如果字符串参数是数字并且可以解析为数字,则此方法将返回一个双精度值。 如果没有,它会抛出异常。
下面是一些内置方法:

  1. Integer.parseInt(String)
  2. Float.parseFloat(String)
  3. Double.parseDouble(String)
  4. Long.parseLong(String)
  5. new BigInteger(String)

如果这些方法没有抛出任何NumberFormatException,那么就意味着解析成功了,而且String是数字的:

public class Numeric {

public static void main(String[] args) {
        String string = "12345.15";
        boolean numeric = true;
        try {
            Double num = Double.parseDouble(string);
        } catch (NumberFormatException e) {
            numeric = false;
        }
        if(numeric)
            System.out.println(string +
" is a number");
        else
            System.out.println(string +
" is not a number");
    }
}
输出:
12345.15 is a number


在上面的程序中,我们有一个名为 string 的String,它包含要检查的字符串。我们还有一个布尔值数字,用于存储最终结果是否为数字。

要检查字符串是否仅包含数字,在 try 块中,我们使用Double的parseDouble()方法将字符串转换为Double。

如果它抛出错误(即NumberFormatException 错误),则意味着该字符串不是数字并且 numeric 设置为false。否则,它是一个数字。

但是,如果要检查是否有多个字符串,则需要将其更改为函数。而且,逻辑基于抛出异常,这可能非常昂贵。

相反,我们可以使用正则表达式的强大功能来检查字符串是否为数字,如下所示。

使用 NumberFormat.parse() 方法
另一种方法是使用NumberFormat.parse()方法来解析给定的字符串。与方法Double.parseDouble()不同,它不会抛出异常;如果没有可以解析的对象,则索引保持不变。

import java.text.NumberFormat;
import java.text.ParsePosition;
public class Main
{
public static boolean isNumeric(String s) {
ParsePosition pos = new ParsePosition(0);
NumberFormat.getInstance().parse(s, pos);
return s.length() == pos.getIndex();
}
public static void main(String[] args)
{
System.out.println(isNumeric("AB")); // false
System.out.println(isNumeric(
"AB12")); // false
System.out.println(isNumeric(
"FF")); // false
System.out.println(isNumeric(
"0xFF")); // false
System.out.println(isNumeric(
" 100")); // false
System.out.println(isNumeric(
"09")); // true
System.out.println(isNumeric(
"100")); // true
System.out.println(isNumeric(
"-100")); // true
System.out.println(isNumeric(
"3.14")); // true
System.out.println(isNumeric(String.valueOf(Integer.MAX_VALUE)));
// true
System.out.println(isNumeric(String.valueOf(Long.MAX_VALUE)));
// true
System.out.println(isNumeric(String.valueOf(Double.MAX_VALUE)));
// true
}
}


使用Guava
在Guava中,你可以使用Double.tryParse()方法将指定的字符串解析为一个双数。然而,与Double.parseDouble()方法不同的是,如果解析失败,它会返回null而不是抛出一个异常。有效的输入正是Double.valueOf(String s)所接受的,除了不允许前导和尾部的空白。

import com.google.common.primitives.Doubles;
 
public class Main
{
    public static boolean isNumeric(String s) {
        return Doubles.tryParse(s) != null;
    }
 
    public static void main(String[] args)
    {
        System.out.println(isNumeric("AB"));        // false
        System.out.println(isNumeric(
"AB12"));      // false
        System.out.println(isNumeric(
"FF"));        // false
        System.out.println(isNumeric(
"0xFF"));      // false
        System.out.println(isNumeric(
"  100"));     // false
 
        System.out.println(isNumeric(
"09"));        // true
        System.out.println(isNumeric(
"100"));       // true
        System.out.println(isNumeric(
"-100"));      // true
        System.out.println(isNumeric(
"3.14"));      // true
        System.out.println(isNumeric(String.valueOf(Integer.MAX_VALUE)));  
// true
        System.out.println(isNumeric(String.valueOf(Long.MAX_VALUE)));      
// true
        System.out.println(isNumeric(String.valueOf(Double.MAX_VALUE)));    
// true
    }
}

使用正则表达式
另一种方法是使用正则表达式。下面提供了基于正则的解决方案,使用正则表达式[-+]?\d*\.?\d+来处理负数和小数。请注意,\d只与[0-9]范围内的数字相匹配,对于其他语言的数字会失败。

我们可以写正则表达式来匹配我们的字符串,并判断它们是否是数字。我们将使用字符串的 matches() 方法来比较字符串的数字和正则表达式。我们将使用正则表达式"-?\\d+(\\.\d+)? "来匹配字符串。让我们试着理解这个正则表达式。

public class Numeric {

public static void main(String[] args) {
        String string = "-1234.15";
        boolean numeric = true;
        numeric = string.matches(
"-?\\d+(\\.\\d+)?");
        if(numeric)
            System.out.println(string +
" is a number");
        else
            System.out.println(string +
" is not a number");
    }
}
Output

-1234.15 is a number

在上面的程序中,我们没有使用try-catch块,而是使用regex来检查字符串是否为数字。这是用String的matches()方法完成的。

在matches()方法中、

  • -? - 这一部分确定给定的数字是否为负数,破折号"-"搜索字面上的破折号,问号"?"标志着它的存在是一个可选的。
  • \\d+ 检查字符串必须至少有1个或更多的数字(\\d)。
  • (\.\d+)?- regex的这一部分是用来识别浮动的数字。这里我们搜索的是一个或多个数字,后面跟着一个句号。结尾处的问号表示小括号内的整个regex部分是可选的,因为一个数字可能不是小数。

我创建了一个简单的方法来检查传入的字符串是否为数字:

public class StringTest {
    public static void main(String[] args) {
        String str = "25";
        String str1 =
"25.06";
        System.out.println(isNumeric(str));
        System.out.println(isNumeric(str1));
    }
private static boolean isNumeric(String str){
        return str != null && str.matches(
"[0-9.]+");
  }
}

isNumeric()方法接受一个字符串作为参数。首先,它检查它是否为空。然后,我们使用matches()方法来检查它是否包含数字0到9和句点字符。

这是检查数字值的一个简单方法。你可以根据你的使用情况,编写或在谷歌上搜索更高级的正则表达式来捕获数字。


内建式与正则表达式的性能
一般来说,Regex版本的性能会比使用内置方法更好。这取决于JVM,但通常这是因为内置方法依赖于抛出和捕获异常,这很昂贵。

在我的机器上,10,000个样本(所有参数都是有效的整数)给出了以下的基准:

Built-in took 15 ms
Regex took 10 ms

如果我引入错误的样本,即10000个样本中的每2个都不是有效的数字,那么内置的数字就会变得更糟,因为有大量的异常处理在进行。

Built-in took 35 ms
Regex took 11 ms


使用Apache Commons库检查一个字符串是否为数字
Apache Commons库提供了一些不同的方法,可以直接用来检查一个字符串是否为数字。让我们分别来看看它们中的每一个。

1、NumberUtils.isCreatable() 方法
isCreatable()是NumberUtils类的一个静态方法,用于检查一个给定的字符串是否是有效的Java数字。一个空的、空的或空白的字符串将返回false。

这个方法接受:

  • 以0x或0X开头的十六进制数
  • 以0开头的八进制数字
  • 科学符号(例如1.05e-10)。
  • 标有类型限定符的数字(例如1L或2.2d)。

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

public class NumberValidation {
  public static boolean isNumeric(String s) {
      return NumberUtils.isCreatable(s);
    }
  public static void main(String args[]) {
      System.out.println("223 is numeric : "+isNumeric("223"));
    }
}

2、NumberUtils.isParsable()方法
NumberUtils.isParsable(String)是NumberUtils类的一个静态方法,用于检查一个给定的String是否是可解析的。

可解析的数字是那些被任何解析方法成功解析的数字,比如Integer.parseInt(String), Long.parseLong(String), Float.parseFloat(String) 或者Double.parseDouble(String)。

与NumberUtils.isCreatable()不同的是,这个方法不接受十六进制数字、科学符号或以任何类型的限定符(如'f'、'F'、'd'、'D'、'l'或'L')结尾的字符串。

3、StringUtils.isNumeric(CharSequence)
StringUtils.isNumeric(CharSequence)检查CharSequence是否只包含Unicode数字。这个方法可以接受任何语言的unicode数字。它不能接受正负符号或小数点,因为小数点不被认为是Unicode数字。

4、StringUtils.isNumericSpace(CharSequence)
StringUtils.isNumericSpace(CharSequence)严格检查Unicode数字和/或空格。这与StringUtils.isNumeric()相同,只是它也接受空格,而且不仅是前导和后导的空格,如果它们在数字之间也接受。