java中如何判断一个字符是否是数字字符,Java判断字符串是否为数字

  java中如何判断一个字符是否是数字字符,Java判断字符串是否为数字

  00-1010序言异常处理正则表达式测试和比较1)n使用NumberFormat数字字符外部工具类1。NumberUtils。Isparsable 2。NumberUtils。IsCreatable3。StringUtils。IsNumeric4。StringUtils。IsNumericSpace乌尔或空字符串2)常规数字、整数、浮点数和负数3)非法字符,如多余的小数点、多余的负号和其他非法格式4)非阿拉伯数字,如印度数字、阿拉伯字符和罗马数字I、II和III 5)非十进制数字,如二进制、八进制、十六进制和科学记数法。

  00-1010判断字符串是否为数字是Java开发中常见的业务需求。实现这种判断的方式有很多种,大致可以分为五类:异常处理、正则表达式、数字字符、NumberFormat工具类、外部工具类。不同类型的实现方式其实略有不同,那么选择哪种方式是最好的呢?本文将逐一列举这五大类中的八个具体方案,并通过丰富的测试案例比较这些方案的差异。相信看完这篇文章你会有自己的思考。

  00-1010使用异常处理的本质是基于java自己对字符串的定义。如果我们的字符串可以转换成数字,那么这个字符串就是一个数字。如果转换失败,就会抛出异常,所以如果我们能捕捉到异常,就可以认为它不是一个数字。这个方案很简单:

  public static boolean isnumeric 1(String str){ try { double . parse double(str);返回true} catch(异常e){返回false}}如果我们的业务只要求判断一个字符串是否为整数,那么我们只需要把double . parse double(str);替换为integer . parse int(str);去做吧。但是这个方案有一个致命的缺陷,因为判断失败会抛出异常。当判断失败的频率比较高时,会出现较大的性能损失。

  00-1010使用正则表达式也是常用的判断方式。下面的正则表达式会判断输入的字符串是整数还是浮点数,涵盖负数。

  public static boolean is numeric 2(String str){ return str!=空字符串匹配(-?\d (\。\d)?);}当然,为了性能考虑,这种方法最好用下面的方式进行优化,因为上面的编写方法在每次调用时都间接在matches内部创建一个模式实例。

  私有静态最终模式NUMBER _ Pattern=Pattern.compile(-?\d (\。\d)?);public static boolean is numeric 2(String str){ return str!=null NUMBER _ pattern . matcher(str)。匹配();}

  00-1010通常使用NumberFormat类的format方法将一个数值格式化成符合某个国家习俗的数值串。比如我们输入18,想输出18人民币,用这个类比较好。在这里,你可以了解到它的具体用法。但是您也可以使用这个类的parse方法来确定输入字符串是否是一个数字。

  public static boolean isnumeric 3(String str){ if(str==null)返回falsenumber format formatter=number format . getinstance();parse position pos=new parse position(0);formatter.parse(str,pos);return str . length()==pos . getindex();}

  00-1010字符串的底层实现实际上是一个字符数组。如果这个字符数组中的每个字符都是一个数字,那么这个字符串不就是一个数字串吗?用Java . lang . character # is digit(int)判断所有字符是否都是数字字符,从而判断数字字符。

  串的目的:

  

 public static boolean isNumeric4(String str) { if (str == null) return false; for (char c : str.toCharArray ()) { if (!Character.isDigit(c)) return false; } return true; }

如果你的java版本是8以上,以上的写法可以替换成如下Stream流的方式,从而看起来更优雅。所以,茴香豆的‘茴’又多了一种写法!

 

  

 public static boolean isNumeric4(String str) { return str != null && str.chars().allMatch(Character::isDigit); }

 

  

外部工具类

使用外部工具类通常需要引入外部jar文件,一般的依赖是apache的comons-lang:

 

  

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

在使用外部工具类时,我们通常使用NumberUtils或者StringUtils,具体如下:

 

  使用NumberUtils时,我们通常会选择其静态方法isParsable和isCreatable其中的一种,它们的不同点在于isCreatable不仅可以接受十进制数字字符串,还可以接受八进制,十六进制以及科学计数法表示的数字字符串,isParsable只接受十进制数字字符串。

  

 

  

1.NumberUtils.isParsable

 public static boolean isNumeric5(String str) { return NumberUtils.isParsable(str); }

 

  

2.NumberUtils.isCreatable

 public static boolean isNumeric6(String str) { return NumberUtils.isCreatable(str); }

如果使用StringUtils,那么要考虑到底该使用isNumeric还是isNumericSpace。二者的唯一差异在于,isNumeric会认为空字符串为非法数字,isNumericSpace则认为空字符串也是数字。

 

  

 

  

3.StringUtils.isNumeric

 public static boolean isNumeric7(String str) { return StringUtils.isNumeric(str); }

 

  

4.StringUtils.isNumericSpace

 public static boolean isNumeric8(String str) { return StringUtils.isNumericSpace(str); }

 

  

测试并比较

默认情况下,文章中的数字都指的是十进制的阿拉伯数字(0 ,1,2 … 9),测试时会扩展范围。考虑以下几个方向:

 

  1)null或者空字符串

  2)常规的数字,整数,浮点数以及负数

  3)包含非法的字符,例如包含多余的小数点,包含多余的负号,以及其它非常规格式

  4)非阿拉伯数字,例如印度数字 १२३,阿拉伯文 ١٢٣,以及罗马数字Ⅰ、Ⅱ、Ⅲ

  5)非十进制的数字,例如二进制,八进制,十六进制,科学计数法表示的数字

  主体测试方法如下:

  

 public static void check(String str) { System.out.println ( "----------checking:【" + str + "】---------" ); System.out.println(isNumeric1(str)); System.out.println(isNumeric2(str)); System.out.println(isNumeric3(str)); System.out.println(isNumeric4(str)); System.out.println(isNumeric5(str)); System.out.println(isNumeric6(str)); System.out.println(isNumeric7(str)); System.out.println(isNumeric8(str)); System.out.println( "---------------end-------------------" ); }

测试用例:

 

  

 public static void main(String[] args) throws ParseException { //1)null或者空字符串 check(null); check(""); check(" "); //2)正常的数字,整数或者浮点数 check("123"); check("0.123"); check("-12.3"); check("07"); //普通十进制7 or 八进制7 //3)包含非法的字符,例如包含多余的小数点,包含多余的负号,以及其它非常规格式 check("123."); check(".123"); check("1.2.3"); check("--12.3"); check("-1-2.3"); check("-12.3-"); check(" 123"); check("1 23"); check("123 "); check("1a2b3c"); check("10.0d"); //double类型 check("1000L"); //long类型 check("10.0f"); //float类型 //4)非阿拉伯数字,例如印度数字 १२३,阿拉伯文 ١٢٣,以及罗马数字Ⅰ、Ⅱ、Ⅲ check("१२३"); check("١٢٣"); check("Ⅲ"); //5)非十进制的数字,例如二进制,八进制,十六进制,科学计数法表示的数字 check("0b100"); //二进制 check("09"); //十进制9 or 非法的八进制 check("0xFF"); //十六进制 check("2.99e+8");//科学计数法 }

由于篇幅原因,这里就不将控制台的打印结果贴上来了,有兴趣的同学可以根据我的代码自己尝试一下。

 

  下面是将测试用例的打印结果做了个表格汇总,表头为方法名,第一列是具体的输入字符串,表格中其它单元格表示该方法判断是否为数字字符串的结果。

  用例isNumberic1isNumberic2isNumberic3isNumberic4isNumberic5isNumberic6isNumberic7isNumberic8nullfalsefalsefalsefalsefalsefalsefalsefalsefalsefalsetruetruefalsefalsefalsetrue" "falsefalsefalsefalsefalsefalsefalsetrue123truetruetruetruetruetruetruetrue0.123truetruetruefalsetruetruefalsefalse-12.3truetruetruefalsetruetruefalsefalse07truetruetruetruetruetruetruetrue123.truefalsetruefalsefalsetruefalsefalse.123truefalsetruefalsetruetruefalsefalse1.2.3falsefalsefalsefalsefalsefalsefalsefalse–12.3falsefalsefalsefalsefalsefalsefalsefalse-1-2.3falsefalsefalsefalsefalsefalsefalsefalse-12.3-falsefalsefalsefalsefalsefalsefalsefalse" 123"truefalsefalsefalsefalsefalsefalsetrue1 23falsefalsefalsefalsefalsefalsefalsetrue"123 "truefalsefalsefalsefalsefalsefalsetrue1a2b3cfalsefalsefalsefalsefalsefalsefalsefalse10.0dtruefalsefalsefalsefalsetruefalsefalse1000Lfalsefalsefalsefalsefalsetruefalsefalse10.0ftruefalsefalsefalsefalsetruefalsefalse१२३falsefalsetruetruetruefalsetruetrue١٢٣falsefalsetruetruetruefalsetruetrueⅢfalsefalsefalsefalsefalsefalsefalsefalse0b100falsefalsefalsefalsefalsefalsefalsefalse09truetruetruetruetruefalsetruetrue0xFFfalsefalsefalsefalsefalsetruefalsefalse2.99e+8truefalsefalsefalsefalsetruefalsefalse通过这个表格,可以看出不同的判断方法,对于非常规的字符串来说,差异还是比较大的。

  

 

  

1)null或者空字符串

在处理null时所有方法保持一致,这也是一个工具类该满足的基本素养。

 

  对于空字符串来说,无论空字符串长度是否大于0,基于StringUtils.isNumericSpace的isNumberic8均会返回true,因为它本身认为空字符串就是数字。

  对于长度大于0的空字符串来说,基于NumberFormat的isNumberic3和基于java.lang.Character#isDigit(int)的isNumberic4 这两种判断方法都正常返回了false。

  但是对于长度为0的空字符串来说,isNumberic3和isNumberic4 这两种判断方法出了点小插曲,它们返回了false。这是因为,对于isNumberic3来说,toCharArray或者chars方法返回长度为0的字符数组,它并没有做一个有效的遍历。对于isNumberic4来说,NumberFormat的起始位置和终点位置一致。

  所以为了让isNumberic3和isNumberic4更加健壮,建议对其实现内部再加一层空字符串的判断,优化后的代码如下。

  

 public static boolean isNumeric3(String str) { if (str == null str.trim ().length() == 0) return false; NumberFormat formatter = NumberFormat.getInstance(); ParsePosition pos = new ParsePosition(0); formatter.parse(str, pos); return str.length() == pos.getIndex(); }
 public static boolean isNumeric4(String str) { if (str == null str.trim ().length() == 0) return false; for (char c : str.toCharArray ()) { if (!Character.isDigit (c)) return false; } return true; }

 

  

2)常规的数字,整数,浮点数以及负数

常规数字指业务中常用的数字,譬如用于表示金额的浮点数,用于统计数量的整数等。这种情况下,isNumberic1,isNumberic2,isNumberic3,isNumberic5,isNumberic6 均表现出一致性,它们判断出来的结果都是相同的,而且也是符合我们常规预期的,是我们认为正确的结果。

 

  对于浮点数,isNumberic4认为这不是有效数字,因为java.lang.Character#isDigit(int)认为小数点并不是数字字符。同样的,基于StringUtils.isNumeric的 isNumberic7 和基于StringUtils.isNumericSpace的 isNumberic8 也返回了false。

  如果我们查看以上两个方法的底层实现,就可以发现 isNumberic7,isNumberic8 和 isNumberic4 的底层实现逻辑都是一样的,它们都是通过判断字符是否为数字字符来实现的。以下是StringUtils.isNumeric和StringUtils.isNumericSpace的源码:

  

 public static boolean isNumeric(CharSequence cs) { if (isEmpty(cs)) { return false; } else { int sz = cs.length(); for(int i = 0; i < sz; ++i) { if (!Character.isDigit(cs.charAt(i))) { return false; } } return true; } } public static boolean isNumericSpace(CharSequence cs) { if (cs == null) { return false; } else { int sz = cs.length(); for(int i = 0; i < sz; ++i) { if (!Character.isDigit(cs.charAt(i)) && cs.charAt(i) != ) { return false; } } return true; } }

这里尤其注意 07这个字符串。在某些语境下,07是十进制的7,在另一些语境下,07是八进制的7。例如我们直接将07赋值个int变量,它确实可以通过编译,但是把07换成09呢?一定会编译失败,这是因为变量声明的场景下,07作为八进制对待,它满足八进制的范围要求,而八进制无法表示09,它太大了,所以编译失败。

 

  但是Double.parseDouble却可以将09转化成9.0,因为这种场景下,输入的数字作为十进制对待,0被忽略了。

  

 int j = 07; int k = 09; //编译失败,非法的八进制 System.out.println (Double.parseDouble ("09")); //打印9.0,以十进制对待

尽管以0开头的数字字符串,在使用Double.parseDouble 的语境中被当作十进制对待,可以被正确解析。但是从某些业务角度或者某种特定思维上来说,数字怎么能以0开始呢?你能接受一个以0开头的整数或者浮点数吗?如果你不能接受这是一个合法的数字字符串,那么很遗憾,现有的案例均不满足需求。你似乎只能通过正则表达式来实现,重新定义你的正则表达式,来过滤掉这类不恰当的字符串。

 

  同时还需要注意,表格倒数第三行的用例是09,和07这一行类似,但isNumberic6在这两行表现的不一致。这是由于isNumberic6使用了NumberUtils.isCreatable,它把以0开头的数字认为是八进制数,符合八进制范围的返回true,不符合的返回false。所以"07"会返回true,09会返回false。

  特别注意,当输入为10.0d, 1000L和10.0f时,在某种程度上也认为这是有效的数字,因为基本类型中声明double,long和float类型的变量时,分别在字面量后面添加一个‘d’(‘D’) ,‘l’(‘L’) 和 ‘f’(‘F’)是一个很常见的操作。 这类声明一般用来处理强制转换,但对于这类数字字符串来说,使用 isNumberic1 的局限性就出来了,本例中基于 Double.parseDouble 来做判断,它可以接受‘d’(‘D’) 和 ‘f’(‘F’) 结尾的数字字符串,但是不能接受以 ‘l’(‘L’) 结尾的数字字符串,以下是Double.parseDouble的部分源码片段。

  

if (var6 >= var5 var6 == var5 - 1 && (var0.charAt(var6) == f var0.charAt(var6) == F var0.charAt(var6) == d var0.charAt(var6) == D)) { if (var13) { return var1 ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO; } return new FloatingDecimal.ASCIIToBinaryBuffer(var1, var3, var21, var8);}

那是不是意味着,如果我们将isNumberic1的内部实现换成Long.parseLong,它就可以接受 1000L 了呢?答案是否定的,如果我们运行以下的代码,系统将抛出异常。

 

  

System.out.println (Long.parseLong ("5562L"));

这是因为Long.parseLong的底层还是用到了Character.digit方法。以下是Long.parseLong的部分源码片段,上述的打印将在第一个if块抛出异常。

 

  

 while (i < len) { // Accumulating negatively avoids surprises near MAX_VALUE digit = Character.digit(s.charAt(i++),radix); if (digit < 0) { throw NumberFormatException.forInputString(s); } if (result < multmin) { throw NumberFormatException.forInputString(s); } result *= radix; if (result < limit + digit) { throw NumberFormatException.forInputString(s); } result -= digit; }

因此,如果你需要接受以‘d’(‘D’),‘f’(‘F’) 和 ‘l’(‘L’)结尾的数字字符串,只有isNumberic6是最优解。

 

  

 

  

3)包含非法的字符,例如包含多余的小数点,包含多余的负号,以及其它非法格式

这部分的用例就相对灵活很多了。极端情况下,比如多一个小数点,或者多一个负号,或者纯粹的掺入非数字字符,isNumberic1 ~ isNumberic8均能做出有效的判断。

 

  但是,如果只有一个小数点,且小数点的位置不合时宜的情况下,比如123.,.123,使用异常处理的isNumberic1和使用NumberFormat的isNumberic3行为一致,均返回了true。

  你可能惊呆了,这怎么能算是有效字符串呢,这种情况其实和07这个测试用例是一样的,Java可以将它们转换成浮点数123.0或者整数123。所以返回true对于java来说这就是合理的。如果你不满意,那只能考虑正则这条路了。

  

 

  

4)非阿拉伯数字,例如印度数字 १२३,阿拉伯文 ١٢٣,以及罗马数字Ⅰ、Ⅱ、Ⅲ

所有的判断方法,均认为罗马数字为非法数字。

 

  使用印度数字或者阿拉伯文数字,其中 isNumberic3,isNumberic4,isNumberic5,isNumberic7,isNumberic8 能够做出有效判断。其它方案均无效。

  如果是做国际业务的同学,你可能就要留意了,他们用本地语言填写的电话号码你涵盖了吗?

  等等,那汉字表示的数字,一,二,三… 该用什么方法来判断呢? 很遗憾,本文列举的方法均不满足,需要自己开发相关工具类或查找有效资料。

  

 

  

5)非十进制的数字,例如二进制,八进制,十六进制,科学计数法表示的数字

前面的测试用例均是十进制数,但是一些少数场景不免会出现十进制以外的数据。二进制变量以 0b或0B 开始,八进制以 0开始,十六进制以0X或0x开始。

 

  通过倒数第二行和倒数第三行可以看出来,只有 isNumberic6 可以准确的判断出八进制和十六进制。

  通过倒数第四行可以看出来,任何方法都不能判断二进制。

  通过最后一行可以看出来,isNumberic1和isNumberic6 可以用来判断科学计数法。

  

 

  

小结

判断一个字符串是否为数字看起来是一项很简单的业务,但是它涉及的场景却是非常多的,从业务角度来看,没有哪个方法是完美的。

 

  有人说异常处理的方式不好,性能低,但是它能处理开头和结尾为空字符串的输入,还能处理科学计数法。

  有人说正则最好,但他们用的正则表达式基本都是从网上扒下来的吧,只能判断阿拉伯数字吧,而且不能处理以0开始的字符吧。

  有人说使用数字字符的方式最好,但是它无法判断浮点数。

  还有人说使用StringUtils最好,那他们有对比过NumberUtils吗?

  总之,没有什么方法是最好的, 最适合的才是最好的。这就和找对象一个道理,你说刘亦菲美吧,她很美,但不适合呀。

  

 

  

总结

到此这篇关于Java判断字符串是否为数字多种方式的文章就介绍到这了,更多相关Java判断字符串是否为数字内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!

 

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: