如何在Java中检查一个字符串是否是数字

1047

在解析字符串之前,如何检查它是否为数字?


42
所有使用正则表达式提出的解决方案都不能用于十六进制数字。 - Oscar Castiblanco
在编程中,传递空字符串到matches(...)函数将会抛出NullPointer异常。 - Hitesh Sahu
请参考Max Malysh的答案,其中提供了一个简洁的Java 8解决方案,无需使用第三方库。 - Andy Thomas
@HiteshSahu 最新版本(包括Java 6.x和7.x)似乎能够优雅地处理null字符串。 - lifebalance
所有提出使用Integer.parseInt()的解决方案都无法正确解析包含“NumberFormatException”的移动电话号码。 - Not a bug
@OscarCastiblanco 在给定的基数中,_所有_字符串都是数字。 - bfontaine
41个回答

1032

通常使用一个简单的用户自定义函数(即编写自己的“isNumeric”函数)来完成此操作。

例如:

public static boolean isNumeric(String str) { 
  try {  
    Double.parseDouble(str);  
    return true;
  } catch(NumberFormatException e){  
    return false;  
  }  
}

然而,如果你频繁调用这个函数,并且预计由于不是数字而导致许多检查失败,则该机制的性能将不佳,因为你依靠每次失败都抛出异常,这是一种相当昂贵的操作。

另一种方法可能是使用正则表达式来检查是否为数字的有效性:

public static boolean isNumeric(String str) {
  return str.matches("-?\\d+(\\.\\d+)?");  //match a number with optional '-' and decimal.
}

请注意上述的正则表达式机制,如果你使用非阿拉伯数字(即0到9之外的数字),它将会失败。这是因为正则表达式中的"\d"部分只匹配[0-9],并不具备国际化数字意识。 (感谢OregonGhost指出!)

或者另一种选择是使用Java内置的java.text.NumberFormat对象来查看,在解析字符串后,解析器位置是否在字符串的末尾。如果是,则可以假定整个字符串是数字:

public static boolean isNumeric(String str) {
  ParsePosition pos = new ParsePosition(0);
  NumberFormat.getInstance().parse(str, pos);
  return str.length() == pos.getIndex();
}

8
Java正则表达式中的\d只匹配拉丁数字吗?如果像.NET正则表达式一样,您将在其他(例如阿拉伯)数字上遇到问题,如此处所解释的:http://blogs.msdn.com/oldnewthing/archive/2004/03/09/86555.aspx - OregonGhost
12
请注意,你的正则表达式中的 . 将匹配任何字符,而不仅仅是小数点分隔符。 - jqno
9
+1 意味着认识到 try/catch 的开销。长期重复使用时,这实际上是一种可怕的方法,但在 Java 中我们确实被困在其中。 - demongolem
3
如果被广泛使用,两种解决方案都不好。抛出异常的成本很高,创建正则表达式的成本也很高。必须创建一次正则表达式并重复使用。 - Daniel Nuriyev
11
注意,不存在所谓的“拉丁数字”,而数字0-9实际上是阿拉伯数字。人们可能熟悉罗马数字,这些数字由说拉丁语的人使用,以I、II、III、IV、V、VI等形式表示。请参考以下维基百科链接:https://en.wikipedia.org/wiki/Arabic_numerals;https://en.wikipedia.org/wiki/Roman_numerals。 - dantiston
显示剩余18条评论

782

70
在这里使用StringUtils.isNumeric()可能不太合适,因为它只检查字符串是否为数字序列。对于大多数整数来说还好,但对于带有小数、组分隔符等的数字则不适用。 - Jeff Mercado
49
不要因为需要一个三行函数而重复造轮子,而不包含整个库。 - dalvarezmartinez1
17
为了实现这个功能,真的值得添加一个整个库吗?显然,如果它与其他东西一起使用,那就很好,但考虑到人们已经用一行代码解决了这个问题,加入整个库可能过度了。 - Water
7
不能用于负数。而一半的数字是负数,所以...... - Paul Draper
8
你是正确的,StringUtils不支持前导符号,但你可以检查NumberUtils.isCreatable,它可以正确处理负数。 - palacsint
显示剩余9条评论

198

Java 8 Lambda 表达式。

String someString = "123123";
boolean isNumeric = someString.chars().allMatch( Character::isDigit );

4
你也可以使用方法引用:someString.chars().allMatch(Character::isDigit)。 - Wienczny
3
不错,但仍然像几乎所有其他“解决方案”一样重复造轮子。此外,在处理'null'时失败(就像几乎所有其他解决方案一样)。 - qben
11
此答案简明扼要、易于理解,几乎可以像英语一样阅读——"字符全部匹配数字"。它不需要第三方库。在非异常情况下,它不会使用异常。这应该成为采纳的答案。 - Andy Thomas
24
"-1"会产生什么结果? - Balázs Németh
11
答案不正确。一个数字字符串可以包含非数字字符(例如 "." 或 "-"),但仍然是完全数值化的。例如,0.5、-1 和 1,000 都会因为这个答案而失败,但它们仍然是完全数值化的。 - Simeon G
显示剩余4条评论

172

如果您使用的是安卓系统,那么您应该使用:

android.text.TextUtils.isDigitsOnly(CharSequence str)

文档在此处

保持简单。大多数人都能“重新编程”(同一件事)。


5
@kape123 :) 当然,"123.456" 不包含数字。 - Ahmed Alejo
10
注意:这会导致空输入出现NPE(空指针异常)。此外,它不能处理负数或小数。 - gMale
2
我喜欢它!!我认为这绝对是针对数字的,而不是针对“.”和“-”。 - illusionJJ
1
这正是我正在寻找的。只检查数字0-9的简单方法。我在EditText的声明中设置了一个过滤器,但以防万一它在未来被更改或替换,有一个简单的编程检查也是很好的。 - jwehrle
为什么这个方法对于空字符串返回true? - YaMiN

134

正如@CraigTP在他出色的回答中提到的那样,我也对使用异常来测试字符串是否为数字存在类似的性能问题。因此我最终选择拆分字符串并使用java.lang.Character.isDigit()

public static boolean isNumeric(String str)
{
    for (char c : str.toCharArray())
    {
        if (!Character.isDigit(c)) return false;
    }
    return true;
}
根据JavadocCharacter.isDigit(char)能够正确识别非拉丁数字。就性能而言,我认为简单比较字符串中每个字符的数量N会比进行正则表达式匹配更加高效。 更新:如Jean-François Corbett 在评论中所指出的,上述代码仅验证正整数,这已经覆盖了大多数情况。下面是更新后的代码,根据系统中使用的默认语言环境正确验证小数,假设字符串中只有一个小数分隔符。
public static boolean isStringNumeric( String str )
{
    DecimalFormatSymbols currentLocaleSymbols = DecimalFormatSymbols.getInstance();
    char localeMinusSign = currentLocaleSymbols.getMinusSign();

    if ( !Character.isDigit( str.charAt( 0 ) ) && str.charAt( 0 ) != localeMinusSign ) return false;

    boolean isDecimalSeparatorFound = false;
    char localeDecimalSeparator = currentLocaleSymbols.getDecimalSeparator();

    for ( char c : str.substring( 1 ).toCharArray() )
    {
        if ( !Character.isDigit( c ) )
        {
            if ( c == localeDecimalSeparator && !isDecimalSeparatorFound )
            {
                isDecimalSeparatorFound = true;
                continue;
            }
            return false;
        }
    }
    return true;
}

2
这个函数会因为负号而失败吗? - java_mouse
1
我认为这应该是被接受的答案,因为它是最轻量级的解决方案。使用异常或正则表达式来检查字符串是否为数字都非常繁重。遍历字符很简单! - W.K.S
1
以上代码接受单个“-”符号并将返回true。更改第一个“if”语句为以下内容:boolean isMinus = str.charAt(0) == localeMinusSign; if ((isMinus && str.length() < 2) || ((!isMinus) && !Character.isDigit(str.charAt(0)))) { return false; } - coder
3
调用 toCharArray() 方法会在 String 对象中创建该数组的副本,因为 String 是不可变的。直接使用 String 对象上的 charAt(int index) 方法可能更快。 - Mike Kucera
2
当传入长度为0的字符串时,会生成“StringIndexOutOfBoundsException”。可以通过if(str.length() == 0) return false;来修复。 - samgak

44

Google的Guava库提供了一个很好的助手方法来实现这一点:Ints.tryParse。你可以像使用Integer.parseInt一样使用它,但如果字符串不能解析为有效的整数,则它将返回null而不是抛出异常。请注意它返回的是Integer而不是int,因此你需要将其转换/自动装箱回int。

示例:

String s1 = "22";
String s2 = "22.2";
Integer oInt1 = Ints.tryParse(s1);
Integer oInt2 = Ints.tryParse(s2);

int i1 = -1;
if (oInt1 != null) {
    i1 = oInt1.intValue();
}
int i2 = -1;
if (oInt2 != null) {
    i2 = oInt2.intValue();
}

System.out.println(i1);  // prints 22
System.out.println(i2);  // prints -1

然而,截至当前版本 - Guava r11 - 它仍被标记为@Beta.

我还没有对它进行基准测试。从源代码来看,有很多的健全性检查开销,但最终它们使用Character.digit(string.charAt(idx)),与@Ibrahim上面的答案类似但略有不同。在它们的实现下,没有异常处理开销。


请注意,如果参数为空,这将抛出NPE异常。 - Vadzim

32

不要使用异常来验证您的值。 而是使用像apache NumberUtils这样的工具库:

NumberUtils.isNumber(myStringValue);

编辑:

请注意,如果你的字符串以0开头,NumberUtils会将其解释为十六进制值。

NumberUtils.isNumber("07") //true
NumberUtils.isNumber("08") //false

9
三年前已经有一个被接受的答案涉及到了 Number.isNumber()。我不做任何解释,只进行翻译。 - Andy Thomas
我不这么认为。它已经更新了或者OP更改了被接受的答案。我记得被接受的答案没有涵盖NumberUtils,所以我添加了我的答案。但是感谢评论。 - Lama
3
已接受答案的历史记录显示,Number.isNumber() 函数在回答的第一个版本中就存在了,该版本的时间为2012年9月24日17:01。 - Andy Thomas
@Goot,这很不错,因为它还涵盖了小数值检查,而StringUtils没有。 - Heena Hussain

29

为什么每个人都在推动异常/正则表达式的解决方案?

虽然我理解大多数人使用try/catch也没有问题,但如果你经常这样做... 它可能会非常耗费精力。

我所做的是将正则表达式、parseNumber()方法和数组搜索方法结合起来,看看哪种方法最有效。这次,我只考虑整数。

public static boolean isNumericRegex(String str) {
    if (str == null)
        return false;
    return str.matches("-?\\d+");
}

public static boolean isNumericArray(String str) {
    if (str == null)
        return false;
    char[] data = str.toCharArray();
    if (data.length <= 0)
        return false;
    int index = 0;
    if (data[0] == '-' && data.length > 1)
        index = 1;
    for (; index < data.length; index++) {
        if (data[index] < '0' || data[index] > '9') // Character.isDigit() can go here too.
            return false;
    }
    return true;
}

public static boolean isNumericException(String str) {
    if (str == null)
        return false;
    try {  
        /* int i = */ Integer.parseInt(str);
    } catch (NumberFormatException nfe) {  
        return false;  
    }
    return true;
}

我得到的速度测试结果如下:

Done with: for (int i = 0; i < 10000000; i++)...

With only valid numbers ("59815833" and "-59815833"):
    Array numeric took 395.808192 ms [39.5808192 ns each]
    Regex took 2609.262595 ms [260.9262595 ns each]
    Exception numeric took 428.050207 ms [42.8050207 ns each]
    // Negative sign
    Array numeric took 355.788273 ms [35.5788273 ns each]
    Regex took 2746.278466 ms [274.6278466 ns each]
    Exception numeric took 518.989902 ms [51.8989902 ns each]
    // Single value ("1")
    Array numeric took 317.861267 ms [31.7861267 ns each]
    Regex took 2505.313201 ms [250.5313201 ns each]
    Exception numeric took 239.956955 ms [23.9956955 ns each]
    // With Character.isDigit()
    Array numeric took 400.734616 ms [40.0734616 ns each]
    Regex took 2663.052417 ms [266.3052417 ns each]
    Exception numeric took 401.235906 ms [40.1235906 ns each]

With invalid characters ("5981a5833" and "a"):
    Array numeric took 343.205793 ms [34.3205793 ns each]
    Regex took 2608.739933 ms [260.8739933 ns each]
    Exception numeric took 7317.201775 ms [731.7201775 ns each]
    // With a single character ("a")
    Array numeric took 291.695519 ms [29.1695519 ns each]
    Regex took 2287.25378 ms [228.725378 ns each]
    Exception numeric took 7095.969481 ms [709.5969481 ns each]

With null:
    Array numeric took 214.663834 ms [21.4663834 ns each]
    Regex took 201.395992 ms [20.1395992 ns each]
    Exception numeric took 233.049327 ms [23.3049327 ns each]
    Exception numeric took 6603.669427 ms [660.3669427 ns each] if there is no if/null check
声明:我不会声称这些方法是100%优化的,它们仅用于演示数据

只有当数字为4个字符或更少并且每个字符串始终是一个数字时才能获胜,如果是这种情况,为什么要进行检查?

简而言之,如果您经常使用try / catch遇到无效数字,则会非常痛苦,这是有道理的。我始终遵循的一个重要规则是永远不要使用try/catch来控制程序流程。这就是一个例子。

有趣的是,简单的if char<0 || >9很容易写,容易记忆(并且应该适用于多种语言),并且几乎赢得了所有测试场景。

唯一的缺点是,我猜想Integer.parseInt()可能会处理非ASCII数字,而数组搜索方法不会。


对于那些想知道我为什么说字符数组方法很容易记住的人,如果您知道没有负号,您可以轻松摆脱像这样压缩的东西:

public static boolean isNumericArray(String str) {
    if (str == null)
        return false;
    for (char c : str.toCharArray())
        if (c < '0' || c > '9')
            return false;
    return true;

最后一点,我对被所有投票接受的示例中的赋值运算符感到好奇。添加赋值

double d = Double.parseDouble(...)

这不仅没有用处,因为您甚至没有使用该值,而且还浪费了处理时间,并使运行时间增加了几个纳秒(导致测试中增加了100-200毫秒)。我看不出为什么有人会这样做,因为这实际上是降低性能的额外工作。

你会认为编译器会优化掉它...虽然也许我应该检查字节码并看看编译器正在做什么。如果它确实被优化掉了,那么这就解释了为什么它对我总是表现得更冗长...因此我想知道怎么回事。请注意:通过“更冗长”,我指运行10000000次迭代的测试,并多次运行该程序(10倍以上),始终显示它的速度较慢。

编辑:更新了Character.isDigit()的一个测试


5
每次都会编译一个新的正则表达式,这不太高效,是吗? - Samuel Edwin Ward
1
@SamuelEdwinWard这就是我发帖的“全部”原因......正则表达式示例使用了其他人提供的答案,并展示了它的低效性。即使您在预先编译正则表达式并仅使用它时,时间差也为:使用其他人提供的正则表达式,需要2587毫秒;预先编译后,只需要950毫秒;如果将其作为数字数组执行,则只需要144毫秒(对于相同字符串的100万次迭代)。预先编译显然会有所帮助,但不幸的是,它仍然比数组方式要差很多...除非有一些疯狂的优化方法我不知道。 - Water
相信正则表达式可以让事情更快几乎是一个谬论。如果只是一次性的搜索,我明白...但我注意到高效编写的代码实际上比正则表达式更出色,足以让你震惊!@Water 很棒的帖子。 - Yo Apps

22
public static boolean isNumeric(String str)
{
    return str.matches("-?\\d+(.\\d+)?");
}

CraigTP的正则表达式(如上所示)会产生一些误报。例如:"23y4"将被视为数字,因为'.'匹配任何字符而不是小数点。

此外,它会拒绝带前导'+'的任何数字。

另一个避免这两个小问题的替代方法是:

public static boolean isNumeric(String str)
{
    return str.matches("[+-]?\\d*(\\.\\d+)?");
}

这将返回true,对于单个加号"+"或减号"-",以及对于"0."则返回false - user85421
单个加号或减号的处理很好。 "0." 是一个有效的数字吗? - user872985
创建正则表达式也是很耗费资源的。必须只创建一次并重复使用。 - Daniel Nuriyev
1
你应该将其更改为 matches("-?\\d+([.]\\d+)?") - Bobs
为什么每个正则表达式的解决方案都假定本地小数格式是“.”(点),而不是像一些欧盟国家那样是“,”(逗号)?! - Yo Apps
显示剩余2条评论

16

我们可以尝试用空格("")替换给定字符串中的所有数字,如果替换后字符串的长度为零,则可以判断该字符串只包含数字。

boolean isNumber(String str){
        if(str.length() == 0)
            return false; //To check if string is empty
        
        if(str.charAt(0) == '-')
            str = str.replaceFirst("-","");// for handling -ve numbers
    
        System.out.println(str);
        
        str = str.replaceFirst("\\.",""); //to check if it contains more than one decimal points
        
        if(str.length() == 0)
            return false; // to check if it is empty string after removing -ve sign and decimal point
        System.out.println(str);
        
        return str.replaceAll("[0-9]","").length() == 0;
    }

1
所以 "" 是一个数字,但 "3.14""-1" 不是? - Eric Duminil
1
显然并不适用于所有数字形式,但是这里给你点赞,因为你有不同的思考方式……如果这个想法是你自己的话。 - gbenroscience

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