Check If a String Is Numeric in Java – 在Java中检查一个字符串是否为数字

最后修改: 2018年 5月 13日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

Oftentimes while operating upon Strings, we need to figure out whether a String is a valid number or not.

在对Strings进行操作时,我们经常需要弄清楚一个String是否是一个有效的数字。

In this tutorial, we’ll explore multiple ways to detect if the given String is numeric, first using plain Java, then regular expressions, and finally by using external libraries.

在本教程中,我们将探索多种方法来检测给定的String是否为数字,首先使用普通Java,然后是正则表达式,最后是使用外部库。

Once we’re done discussing various implementations, we’ll use benchmarks to get an idea of which methods are optimal.

一旦我们完成了对各种实现方式的讨论,我们将使用基准来了解哪些方法是最佳的。

2. Prerequisites

2.先决条件

Let’s start with some prerequisites before we head on to the main content.

在我们进入主要内容之前,让我们从一些先决条件开始。

In the latter part of this article, we’ll be using Apache Commons external library to add its dependency in our pom.xml:

在本文的后半部分,我们将使用Apache Commons外部库,在我们的pom.xml中添加其依赖性。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

The latest version of this library can be found on Maven Central.

该库的最新版本可以在Maven Central上找到。

3. Using Plain Java

3.使用普通的Java

Perhaps the easiest and the most reliable way to check whether a String is numeric or not is by parsing it using Java’s built-in methods:

也许检查一个String是否为数字的最简单和最可靠的方法是使用Java的内置方法对其进行解析。

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

If these methods don’t throw any NumberFormatException, then it means that the parsing was successful and the String is numeric:

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

public static boolean isNumeric(String strNum) {
    if (strNum == null) {
        return false;
    }
    try {
        double d = Double.parseDouble(strNum);
    } catch (NumberFormatException nfe) {
        return false;
    }
    return true;
}

Let’s see this method in action:

让我们看看这个方法的实际效果。

assertThat(isNumeric("22")).isTrue();
assertThat(isNumeric("5.05")).isTrue();
assertThat(isNumeric("-200")).isTrue(); 
assertThat(isNumeric("10.0d")).isTrue();
assertThat(isNumeric("   22   ")).isTrue();
 
assertThat(isNumeric(null)).isFalse();
assertThat(isNumeric("")).isFalse();
assertThat(isNumeric("abc")).isFalse();

In our isNumeric() method, we’re just checking for values that are of type Double; however, we can also modify this method to check for Integer, Float, Long, and large numbers by using any of the parse methods that we enlisted earlier.

在我们的isNumeric()方法中,我们只是检查Double类型的值;但是,我们也可以通过使用我们之前列举的任何解析方法来修改这个方法以检查IntegerFloatLong以及大数。

These methods are also discussed in the Java String Conversions article.

这些方法在Java字符串转换文章中也有讨论。

4. Using Regular Expressions

4.使用正则表达式

Now let’s use regex -?\d+(\.\d+)? to match numeric Strings consisting of the positive or negative integer and floats.

现在让我们用regex -?\d+(\.\d+)?来匹配由正负整数和浮点数组成的数字字符串

It goes without saying that we can definitely modify this regex to identify and handle a wide range of rules. Here, we’ll keep it simple.

不言而喻,我们肯定可以修改这个词组来识别和处理各种规则。在这里,我们将保持简单。

Let’s break down this regex and see how it works:

让我们来分析一下这个词组,看看它是如何工作的。

  • -? – this part identifies if the given number is negative, the dash “” searches for dash literally and the question mark “?” marks its presence as an optional one
  • \d+ – this searches for one or more digits
  • (\.\d+)? – this part of regex is to identify float numbers. Here we’re searching for one or more digits followed by a period. The question mark, in the end, signifies that this complete group is optional.

Regular expressions are a very broad topic. To get a brief overview, check our tutorial on the Java regular expressions API.

正则表达式是一个非常广泛的话题。要获得一个简单的概述,请查看我们关于Java正则表达式API的教程。

For now, let’s create a method using the above regular expression:

现在,让我们使用上述正则表达式创建一个方法。

private Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?");

public boolean isNumeric(String strNum) {
    if (strNum == null) {
        return false; 
    }
    return pattern.matcher(strNum).matches();
}

Now let’s look at some assertions for the above method:

现在我们来看看上述方法的一些断言。

assertThat(isNumeric("22")).isTrue();
assertThat(isNumeric("5.05")).isTrue();
assertThat(isNumeric("-200")).isTrue();

assertThat(isNumeric(null)).isFalse();
assertThat(isNumeric("abc")).isFalse();

 5. Using Apache Commons

5.使用Apache Commons

In this section, we’ll discuss various methods available in the Apache Commons library.

在本节中,我们将讨论Apache Commons库中的各种方法。

5.1. NumberUtils.isCreatable(String) 

5.1.NumberUtils.isCreatable(String)

NumberUtils from Apache Commons provides a static method NumberUtils.isCreatable(String), which checks whether a String is a valid Java number or not.

NumberUtils来自Apache Commons的静态方法NumberUtils.isCreatable(String)它检查一个String是否是一个有效的Java数字。

This method accepts:

这个方法接受。

  1. Hexadecimal numbers starting with 0x or 0X
  2. Octal numbers starting with a leading 0
  3. Scientific notation (for example 1.05e-10)
  4. Numbers marked with a type qualifier (for example 1L or 2.2d)

If the supplied string is null or empty/blank, then it’s not considered a number and the method will return false.

如果提供的字符串是nullempty/blank,那么它就不被视为一个数字,该方法将返回false

Let’s run some tests using this method:

让我们用这种方法进行一些测试。

assertThat(NumberUtils.isCreatable("22")).isTrue();
assertThat(NumberUtils.isCreatable("5.05")).isTrue();
assertThat(NumberUtils.isCreatable("-200")).isTrue();
assertThat(NumberUtils.isCreatable("10.0d")).isTrue();
assertThat(NumberUtils.isCreatable("1000L")).isTrue();
assertThat(NumberUtils.isCreatable("0xFF")).isTrue();
assertThat(NumberUtils.isCreatable("07")).isTrue();
assertThat(NumberUtils.isCreatable("2.99e+8")).isTrue();
 
assertThat(NumberUtils.isCreatable(null)).isFalse();
assertThat(NumberUtils.isCreatable("")).isFalse();
assertThat(NumberUtils.isCreatable("abc")).isFalse();
assertThat(NumberUtils.isCreatable(" 22 ")).isFalse();
assertThat(NumberUtils.isCreatable("09")).isFalse();

Note that we’re getting true assertions for hexadecimal numbers, octal numbers and scientific notations in lines 6, 7 and 8, respectively.

注意,我们在第6、7、8行分别得到了十六进制数、八进制数和科学符号的断言。

Also, on line 14, the string “09” returns false because the preceding “0” indicates that this is an octal number, and “09” is not a valid octal number.

另外,在第14行,字符串“09”返回false,因为前面的“0”表示这是一个八进制数,而“09”不是一个有效的八进制数。

For every input that returns true with this method, we can use NumberUtils.createNumber(String), which will give us the valid number.

对于每个用这个方法返回true的输入,我们可以使用NumberUtils.createNumber(String),这将给我们提供有效的数字。

5.2. NumberUtils.isParsable(String) 

5.2.NumberUtils.isParsable(String)

The NumberUtils.isParsable(String) method checks whether the given String is parsable or not.

NumberUtils.isParsable(String)方法检查给定的String是否是可解析的。

Parsable numbers are those that are parsed successfully by any parse method like Integer.parseInt(String), Long.parseLong(String), Float.parseFloat(String) or Double.parseDouble(String).

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

Unlike NumberUtils.isCreatable(), this method won’t accept hexadecimal numbers, scientific notations, or strings ending with any type of qualifier like ‘f’, ‘F’, ‘d’ ,’D’ ,’l’or‘L’.

NumberUtils.isCreatable()不同,该方法不接受十六进制数字、科学符号或以任何类型的修饰语结尾的字符串,如‘f’, ‘F’, ‘d’ , ‘D’ , ‘l’‘L’

Let’s look at some affirmations:

让我们看看一些肯定的说法。

assertThat(NumberUtils.isParsable("22")).isTrue();
assertThat(NumberUtils.isParsable("-23")).isTrue();
assertThat(NumberUtils.isParsable("2.2")).isTrue();
assertThat(NumberUtils.isParsable("09")).isTrue();

assertThat(NumberUtils.isParsable(null)).isFalse();
assertThat(NumberUtils.isParsable("")).isFalse();
assertThat(NumberUtils.isParsable("6.2f")).isFalse();
assertThat(NumberUtils.isParsable("9.8d")).isFalse();
assertThat(NumberUtils.isParsable("22L")).isFalse();
assertThat(NumberUtils.isParsable("0xFF")).isFalse();
assertThat(NumberUtils.isParsable("2.99e+8")).isFalse();

On line 4, unlike NumberUtils.isCreatable(), the number starting with string “0” isn’t considered an octal number, but a normal decimal number, and therefore it returns true.

在第4行,与NumberUtils.isCreatable()不同,以字符串“0”开始的数字不被认为是八进制数字,而是一个正常的十进制数字,因此它返回true。

We can use this method as a replacement for what we did in section 3, where we’re trying to parse a number and checking for an error.

我们可以用这个方法来替代我们在第3节中所做的,即我们试图解析一个数字并检查是否有错误。

5.3. StringUtils.isNumeric(CharSequence) 

5.3.StringUtils.isNumeric(CharSequence)

The method StringUtils.isNumeric(CharSequence) checks strictly for Unicode digits. This means:

该方法StringUtils.isNumeric(CharSequence)严格检查Unicode数字。这意味着。

  1. Any digits from any language that is a Unicode digit is acceptable
  2. Since a decimal point is not considered as a Unicode digit, it’s not valid
  3. Leading signs (either positive or negative) are also not acceptable

Now let’s see this method in action:

现在让我们看看这个方法的运作情况。

assertThat(StringUtils.isNumeric("123")).isTrue();
assertThat(StringUtils.isNumeric("١٢٣")).isTrue();
assertThat(StringUtils.isNumeric("१२३")).isTrue();
 
assertThat(StringUtils.isNumeric(null)).isFalse();
assertThat(StringUtils.isNumeric("")).isFalse();
assertThat(StringUtils.isNumeric("  ")).isFalse();
assertThat(StringUtils.isNumeric("12 3")).isFalse();
assertThat(StringUtils.isNumeric("ab2c")).isFalse();
assertThat(StringUtils.isNumeric("12.3")).isFalse();
assertThat(StringUtils.isNumeric("-123")).isFalse();

Note that the input parameters in lines 2 and 3 are representing numbers 123 in Arabic and Devanagari, respectively. Since they’re valid Unicode digits, this method returns true on them.

请注意,第2行和第3行中的输入参数分别代表阿拉伯数字123和Devanagari数字。由于它们是有效的Unicode数字,这个方法对它们返回true

5.4. StringUtils.isNumericSpace(CharSequence)

5.4.StringUtils.isNumericSpace(CharSequence)

The StringUtils.isNumericSpace(CharSequence) checks strictly for Unicode digits and/or space. This is the same as StringUtils.isNumeric() except that it also accepts spaces, and not only leading and trailing spaces, but also if they’re in between numbers:

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

assertThat(StringUtils.isNumericSpace("123")).isTrue();
assertThat(StringUtils.isNumericSpace("١٢٣")).isTrue();
assertThat(StringUtils.isNumericSpace("")).isTrue();
assertThat(StringUtils.isNumericSpace("  ")).isTrue();
assertThat(StringUtils.isNumericSpace("12 3")).isTrue();
 
assertThat(StringUtils.isNumericSpace(null)).isFalse();
assertThat(StringUtils.isNumericSpace("ab2c")).isFalse();
assertThat(StringUtils.isNumericSpace("12.3")).isFalse();
assertThat(StringUtils.isNumericSpace("-123")).isFalse();

6. Benchmarks

6.基准

Before we conclude this article, let’s go through some benchmark results to help us to analyze which of the above mentioned methods are best for our use-case.

在结束本文之前,让我们通过一些基准结果来帮助我们分析上述哪种方法最适合我们的用例。

6.1. Simple Benchmark

6.1.简单的基准测试

First, we take a simple approach. We pick one string value – for our test we use Integer.MAX_VALUE. That value will then be tested against all our implementations:

首先,我们采取一个简单的方法。我们选择一个字符串值 – 对于我们的测试,我们使用Integer.MAX_VALUE。然后这个值将在我们所有的实现中被测试。

Benchmark                                     Mode  Cnt    Score   Error  Units
Benchmarking.usingCoreJava                    avgt   20   57.241 ± 0.792  ns/op
Benchmarking.usingNumberUtils_isCreatable     avgt   20   26.711 ± 1.110  ns/op
Benchmarking.usingNumberUtils_isParsable      avgt   20   46.577 ± 1.973  ns/op
Benchmarking.usingRegularExpressions          avgt   20  101.580 ± 4.244  ns/op
Benchmarking.usingStringUtils_isNumeric       avgt   20   35.885 ± 1.691  ns/op
Benchmarking.usingStringUtils_isNumericSpace  avgt   20   31.979 ± 1.393  ns/op

As we can see, the most costly operations are regular expressions. After that is our core Java-based solution.

我们可以看到,最昂贵的操作是正则表达式。之后是我们基于Java的核心解决方案。

Moreover, note that the operations using the Apache Commons library are by-and-large the same.

此外,请注意,使用Apache Commons库的操作基本上是一样的。

6.2. Enhanced Benchmark

6.2.强化基准测试

Let’s use a more diverse set of tests for a more representative benchmark:

让我们使用一套更多样化的测试,以获得更具代表性的基准。

  • 95 values are numeric (0-94 and Integer.MAX_VALUE)
  • 3 contain numbers but are still malformatted — ‘x0‘, ‘0..005′, and ‘–11
  • 1 contains only text
  • 1 is a null

Upon executing the same tests, we’ll see the results:

执行同样的测试后,我们会看到结果。

Benchmark                                     Mode  Cnt      Score     Error  Units
Benchmarking.usingCoreJava                    avgt   20  10162.872 ± 798.387  ns/op
Benchmarking.usingNumberUtils_isCreatable     avgt   20   1703.243 ± 108.244  ns/op
Benchmarking.usingNumberUtils_isParsable      avgt   20   1589.915 ± 203.052  ns/op
Benchmarking.usingRegularExpressions          avgt   20   7168.761 ± 344.597  ns/op
Benchmarking.usingStringUtils_isNumeric       avgt   20   1071.753 ±   8.657  ns/op
Benchmarking.usingStringUtils_isNumericSpace  avgt   20   1157.722 ±  24.139  ns/op

The most important difference is that two of our tests, the regular expressions solution and the core Java-based solution, have traded places.

最重要的区别是,我们的两个测试,即正则表达式解决方案和基于Java的核心解决方案,已经交换了位置。

From this result, we learn that throwing and handling of the NumberFormatException, which occurs in only 5% of the cases, has a relatively big impact on the overall performance. So we can conclude that the optimal solution depends on our expected input.

从这个结果中,我们了解到抛出和处理NumberFormatException(只发生在5%的情况下)对整体性能有比较大的影响。所以我们可以得出结论,最佳解决方案取决于我们的预期输入。

Also, we can safely conclude that we should use the methods from the Commons library or a method implemented similarly for optimal performance.

另外,我们可以有把握地得出结论,我们应该使用Commons库中的方法或类似实现的方法来获得最佳性能。

7. Conclusion

7.结论

In this article, we explored different ways to find if a String is numeric or not. We looked at both solutions – built-in methods and external libraries.

在这篇文章中,我们探讨了查找String是否为数字的不同方法。我们研究了两种解决方案–内置方法和外部库。

As always, the implementation of all examples and code snippets given above, including the code used to perform benchmarks, can be found over on GitHub.

一如既往,上面给出的所有示例和代码片段的实现,包括用于执行基准的代码,都可以在GitHub上找到