你知道Java判断字符串是否为数字的多种方式吗

目录
  • 前言
  • 异常处理
  • 正则表达式
  • 使用NumberFormat
  • 数字字符
  • 外部工具类
    • 1.NumberUtils.isParsable
    • 2.NumberUtils.isCreatable
    • 3.StringUtils.isNumeric
    • 4.StringUtils.isNumericSpace
  • 测试并比较
    • 1)null或者空字符串
    • 2)常规的数字,整数,浮点数以及负数
    • 3)包含非法的字符,例如包含多余的小数点,包含多余的负号,以及其它非法格式
    • 4)非阿拉伯数字,例如印度数字 १२३,阿拉伯文 ١٢٣,以及罗马数字Ⅰ、Ⅱ、Ⅲ
    • 5)非十进制的数字,例如二进制,八进制,十六进制,科学计数法表示的数字
  • 小结
  • 总结

前言

判断一个字符串是否为数字是Java开发中很常见的业务需求,实现这个判断有很多种方式,大体上分为异常处理,正则表达式,数字字符,NumberFormat工具类,外部工具类这五大类,不同类型下的实现方式其实略有不同,那么究竟选择哪种方式才是最好的呢?本文将一一列举出这5类中具体8个方案,并通过丰富的测试用例来并对比这些方案的差异,相信看完本文,你将会有自己的思考。

异常处理

使用异常处理的本质是基于java自身对于字符串定义而实现的,如果我们的字符串能够被转换为数字,那么这个字符串就是数字,如果转换失败则会抛出异常,所以我们如果能够捕获异常,就可以认为它不是数字,这个方案很简单:

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

如果我们的业务只要求判断字符串是否为整数,那么只需要将Double.parseDouble(str);换成Integer.parseInt(str);即可。但是这个方案有个致命缺陷,由于判断失败会抛异常出来,当判断失败的频率比较高,将产生较大的性能损耗。

正则表达式

使用正则表达式也是一种常见的判断方式,以下的正则表达式将判断输入字符串是否为整数或者浮点数,涵盖负数的情况。

    public static boolean isNumeric2(String str) {
        return str != null && str.matches("-?\\d+(\\.\\d+)?");
    }

当然,为了性能考量,这个方法最好优化成以下方式,因为上面的写法每次调用时都会在matches内部间接创建一个Pattern实例。

    private static final Pattern NUMBER_PATTERN = Pattern.compile("-?\\d+(\\.\\d+)?");
    public static boolean isNumeric2(String str) {
        return str != null && NUMBER_PATTERN.matcher(str).matches();
    }

使用NumberFormat

通常使用NumberFormat类的format方法将一个数值格式化为符合某个国家地区习惯的数值字符串,例如我们输入18,希望输出18¥,使用这个类再好不过了,这里可以了解它的具体用法。但是也可以用该类的parse方法来判断输入字符串是否为数字。

    public static boolean isNumeric3(String str) {
        if (str == null) return false;
        NumberFormat formatter = NumberFormat.getInstance();
        ParsePosition pos = new ParsePosition(0);
        formatter.parse(str, pos);
        return str.length() == pos.getIndex();
    }

数字字符

字符串的底层实现其实就是字符数组,如果这个字符数组中每个字符都是数字,那么这个字符串不就是数字字符串了吗?利用java.lang.Character#isDigit(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");//科学计数法
    }

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

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

用例 isNumberic1 isNumberic2 isNumberic3 isNumberic4 isNumberic5 isNumberic6 isNumberic7 isNumberic8
null false false false false false false false false
“” false false true true false false false true
" " false false false false false false false true
“123” true true true true true true true true
“0.123” true true true false true true false false
“-12.3” true true true false true true false false
“07” true true true true true true true true
“123.” true false true false false true false false
“.123” true false true false true true false false
“1.2.3” false false false false false false false false
“–12.3” false false false false false false false false
“-1-2.3” false false false false false false false false
“-12.3-” false false false false false false false false
" 123" true false false false false false false true
“1 23” false false false false false false false true
"123 " true false false false false false false true
“1a2b3c” false false false false false false false false
“10.0d” true false false false false true false false
“1000L” false false false false false true false false
“10.0f” true false false false false true false false
“१२३” false false true true true false true true
“١٢٣” false false true true true false true true
“Ⅲ” false false false false false false false false
“0b100” false false false false false false false false
“09” true true true true true false true true
“0xFF” false false false false false true false false
“2.99e+8” true false false false false true false false

通过这个表格,可以看出不同的判断方法,对于非常规的字符串来说,差异还是比较大的。

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判断字符串是否为数字内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java判断字符串为空、字符串是否为数字

    关于 String 的判空: 复制代码 代码如下: //这是对的if (selection != null && !selection.equals("")) {      whereClause += selection;  } //这是错的if (!selection.equals("") && selection != null) {      whereClause += selection;  } 注:"==&qu

  • java判断字符串是否为数字的方法小结

    本文实例总结了java判断字符串是否为数字的方法.分享给大家供大家参考,具体如下: 方法一:用JAVA自带的函数 public static boolean isNumeric(String str){ for (int i = str.length();--i>=0;){ if (!Character.isDigit(str.charAt(i))){ return false; } } return true; } 方法二:用正则表达式 public static boolean isNume

  • Java判断字符串是不是数字过程解析

    前言 数字是计算机科学的根本,那么java中数字是怎样来表示呢?规则又是怎样呢?今天我们来探讨一下这个话题.数字在某些领域经常用字符串来进行表示和传递.那么我们可以从判断java中一个字符串是否是数字来获取一些有用的信息. 空字符和null 首先我们可以很清晰的知道空字符""和null 肯定不是数字.其实我们写其他逻辑一样.把一些最极端最容易辨别处理的逻辑判断直接要优先进行处理.这是一个小的窍门. toCharArray 排除了先前情况的字符串可以通过toCharArray() 方法转

  • java基础-给出一个随机字符串,判断有多少字母?多少数字?

    我这里用到了String类中的toarray[]方法. 当看到字符串和判断,我想到之前学过的c语言中判断字符数组中元素,我就去API中找字符串转换成数组的方法 实现方法不唯一,此方法仅作初学者(自己)参考..... 在String类中一共找到三个转数组的方法 很显然,第三个是想要的方法. 实现代码: package com.string; import java.util.Scanner; public class Character_Judge { public static void mai

  • Java用正则对字符串进行处理并判断是否能转为数字

    代码如下所示: package java_test; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author: gznc_pcc * @date:2018年6月1日 10:50:38 * @version : * */ class Main { public static void main(String[] args) { String lineString = "[\"1\"

  • 你知道Java判断字符串是否为数字的多种方式吗

    目录 前言 异常处理 正则表达式 使用NumberFormat 数字字符 外部工具类 1.NumberUtils.isParsable 2.NumberUtils.isCreatable 3.StringUtils.isNumeric 4.StringUtils.isNumericSpace 测试并比较 1)null或者空字符串 2)常规的数字,整数,浮点数以及负数 3)包含非法的字符,例如包含多余的小数点,包含多余的负号,以及其它非法格式 4)非阿拉伯数字,例如印度数字 १२३,阿拉伯文 ١٢

  • java从字符串中提取数字的简单实例

    随便给你一个含有数字的字符串,比如: String s="eert343dfg56756dtry66fggg89dfgf"; 那我们如何把其中的数字提取出来呢?大致有以下几种方法,正则表达式,集合类,还有就是String类提供的方法. 1 String类提供的方法: package 测试练习; import Java.util.*; public class get_StringNum { /** *2016.10.25 */ public static void main(Strin

  • Java判断字符串是否为IP地址的方法

    Java 判断字符串是否为IP地址,供大家参考,具体内容如下 1.代码 主要就是这么几个条件 非空 长度符合 0.0.0.0 - 255.255.255.255 包含分隔符 且 个数正确 四个全部是数字,且都在合理的范围内 /** * 判断某个字符串是否是一个 IP 地址 * * @param str 字符串 */ public static boolean isIpStr(String str) { // 非空 // boolean notBlank = StringUtils.isNotBl

  • IOS判断字符串是不是纯数字的方法总结

    前言 在大家开发项目的时候,遇到需求可能是让我们只输入一段纯数字,这时候我们就要对这个字符串进行筛选判断,不符合纯数字进行提示操作,以求达到最好的交互效果也能满足需求. 下面介绍几种判断字符串是否为纯数字的方法 第一种方式是使用NSScanner: 1. 整形判断 - (BOOL)isPureInt:(NSString *)string{ NSScanner* scan = [NSScanner scannerWithString:string]; int val; return [scan s

  • java判断字符串中是否包含中文并过滤中文

    java判断字符串中是否包含中文并过滤掉中文,具体内容如下 1.判断字符串中是否包含中文方法封装 /** * 判断字符串中是否包含中文 * @param str * 待校验字符串 * @return 是否为中文 * @warn 不能校验是否为中文标点符号 */ public static boolean isContainChinese(String str) { Pattern p = Pattern.compile("[\u4e00-\u9fa5]"); Matcher m = p

  • javaScript 判断字符串是否为数字的简单方法

    复制代码 代码如下: parseInt("Hello",10);//return NAN   parseInt("110",10);//return 110 所以简单的判断字符串是否为数字的方法就是利用isNaN(),如果返回true,则该字符串不为数字,否则为数字 复制代码 代码如下: isNaN(parseInt("Hello",10));//true;   isNaN(parseInt("110",10));//fals

  • 利用JS判断字符串是否含有数字与特殊字符的方法小结

    前言 本文主要介绍的是利用JS判断字符串是否含有数字与特殊字符的方法,文中有几种不同的方法,包括普通的JS验证法.正则表达式法,另外还有判断是否为浮点数的js函数,在最后还将简要介绍下isNAN函数的使用方法和例子,来一起学习学习吧. 一.正则表达式方法判断是否为数字,包括判断正整数: function checkRate(input) { var re = /^[0-9]+.?[0-9]*$/; //判断字符串是否为数字,//若判断正整数,则后边是:/^[1-9]+[0-9]*]*$/ if

  • C#判断字符串是否是数字(实例)

    话不多说,请看代码 /// <summary> /// 判断字符串是否是数字 /// </summary> public static bool IsNumber(string s) { if (string.IsNullOrWhiteSpace(s)) return false; const string pattern = "^[0-9]*$"; Regex rx = new Regex(pattern); return rx.IsMatch(s); } 以

随机推荐