如何在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个回答

15
你可以使用 NumberFormat#parse 方法:
try
{
     NumberFormat.getInstance().parse(value);
}
catch(ParseException e)
{
    // Not a number.
}

提供了一个编辑 - .getInstance() 丢失了。+1,因为这是我在找到这个问题时选择的答案。 - 8bitjunkie
7
如果过度使用,成本高昂。 - Daniel Nuriyev
1
如果在“value”的末尾有垃圾字符,它也会通过。 - Brian White
如果您不记录异常,这将会创建一个声纳问题。 - jmhostalet
1
这适用于数字格式0x0001,其中Double.parseDouble无法工作。+1 - Seabass77

11

已经发布在这里:https://dev59.com/QXNA5IYBdhLWcg3wEZiT#20154903 请查看评论以了解此解决方案存在的一些问题。 - Adam Burley

8

这是我对问题的回答。

一种捕获所有便利方法,您可以使用它来解析任何类型的解析器所需的任何字符串:isParsable(Object parser, String str)。解析器可以是一个Class或一个object。这也允许您使用自己编写的自定义解析器,并且应该适用于所有情况,例如:

isParsable(Integer.class, "11");
isParsable(Double.class, "11.11");
Object dateFormater = new java.text.SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z");
isParsable(dateFormater, "2001.07.04 AD at 12:08:56 PDT");

这是我的代码,包括方法描述。

import java.lang.reflect.*;

/**
 * METHOD: isParsable<p><p>
 * 
 * This method will look through the methods of the specified <code>from</code> parameter
 * looking for a public method name starting with "parse" which has only one String
 * parameter.<p>
 * 
 * The <code>parser</code> parameter can be a class or an instantiated object, eg:
 * <code>Integer.class</code> or <code>new Integer(1)</code>. If you use a
 * <code>Class</code> type then only static methods are considered.<p>
 * 
 * When looping through potential methods, it first looks at the <code>Class</code> associated
 * with the <code>parser</code> parameter, then looks through the methods of the parent's class
 * followed by subsequent ancestors, using the first method that matches the criteria specified
 * above.<p>
 * 
 * This method will hide any normal parse exceptions, but throws any exceptions due to
 * programmatic errors, eg: NullPointerExceptions, etc. If you specify a <code>parser</code>
 * parameter which has no matching parse methods, a NoSuchMethodException will be thrown
 * embedded within a RuntimeException.<p><p>
 * 
 * Example:<br>
 * <code>isParsable(Boolean.class, "true");<br>
 * isParsable(Integer.class, "11");<br>
 * isParsable(Double.class, "11.11");<br>
 * Object dateFormater = new java.text.SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z");<br>
 * isParsable(dateFormater, "2001.07.04 AD at 12:08:56 PDT");<br></code>
 * <p>
 * 
 * @param parser    The Class type or instantiated Object to find a parse method in.
 * @param str   The String you want to parse
 * 
 * @return true if a parse method was found and completed without exception
 * @throws java.lang.NoSuchMethodException If no such method is accessible 
 */
public static boolean isParsable(Object parser, String str) {
    Class theClass = (parser instanceof Class? (Class)parser: parser.getClass());
    boolean staticOnly = (parser == theClass), foundAtLeastOne = false;
    Method[] methods = theClass.getMethods();

    // Loop over methods
    for (int index = 0; index < methods.length; index++) {
        Method method = methods[index];

        // If method starts with parse, is public and has one String parameter.
        // If the parser parameter was a Class, then also ensure the method is static. 
        if(method.getName().startsWith("parse") &&
            (!staticOnly || Modifier.isStatic(method.getModifiers())) &&
            Modifier.isPublic(method.getModifiers()) &&
            method.getGenericParameterTypes().length == 1 &&
            method.getGenericParameterTypes()[0] == String.class)
        {
            try {
                foundAtLeastOne = true;
                method.invoke(parser, str);
                return true; // Successfully parsed without exception
            } catch (Exception exception) {
                // If invoke problem, try a different method
                /*if(!(exception instanceof IllegalArgumentException) &&
                   !(exception instanceof IllegalAccessException) &&
                   !(exception instanceof InvocationTargetException))
                        continue; // Look for other parse methods*/

                // Parse method refuses to parse, look for another different method
                continue; // Look for other parse methods
            }
        }
    }

    // No more accessible parse method could be found.
    if(foundAtLeastOne) return false;
    else throw new RuntimeException(new NoSuchMethodException());
}


/**
 * METHOD: willParse<p><p>
 * 
 * A convienence method which calls the isParseable method, but does not throw any exceptions
 * which could be thrown through programatic errors.<p>
 * 
 * Use of {@link #isParseable(Object, String) isParseable} is recommended for use so programatic
 * errors can be caught in development, unless the value of the <code>parser</code> parameter is
 * unpredictable, or normal programtic exceptions should be ignored.<p>
 * 
 * See {@link #isParseable(Object, String) isParseable} for full description of method
 * usability.<p>
 * 
 * @param parser    The Class type or instantiated Object to find a parse method in.
 * @param str   The String you want to parse
 * 
 * @return true if a parse method was found and completed without exception
 * @see #isParseable(Object, String) for full description of method usability 
 */
public static boolean willParse(Object parser, String str) {
    try {
        return isParsable(parser, str);
    } catch(Throwable exception) {
        return false;
    }
}

7
为了匹配只包含ASCII数字的正十进制整数,请使用以下代码:

```^[0-9]+$```

public static boolean isNumeric(String maybeNumeric) {
    return maybeNumeric != null && maybeNumeric.matches("[0-9]+");
}

7
一个避免使用try-catch并处理负数和科学计数法的高效方法。
Pattern PATTERN = Pattern.compile( "^(-?0|-?[1-9]\\d*)(\\.\\d+)?(E\\d+)?$" );

public static boolean isNumeric( String value ) 
{
    return value != null && PATTERN.matcher( value ).matches();
}

6
正则表达式匹配
这里有一个升级版的"CraigTP"正则表达式匹配,增加了更多的验证。
public static boolean isNumeric(String str)
{
    return str.matches("^(?:(?:\\-{1})?\\d+(?:\\.{1}\\d+)?)$");
}
  1. 只允许有一个负号-,且必须在开头。
  2. 负号后面必须是数字。
  3. 只允许有一个小数点.
  4. 小数点后面必须是数字。

正则表达式测试

1                  --                   **VALID**
1.                 --                   INVALID
1..                --                   INVALID
1.1                --                   **VALID**
1.1.1              --                   INVALID

-1                 --                   **VALID**
--1                --                   INVALID
-1.                --                   INVALID
-1.1               --                   **VALID**
-1.1.1             --                   INVALID

6
这是一个检查字符串是否为数字的类,还可以修复数字字符串:

特性:

  1. 去除不必要的零 ["12.0000000" -> "12"]
  2. 去除不必要的零 ["12.0580000" -> "12.058"]
  3. 去除非数字字符 ["12.00sdfsdf00" -> "12"]
  4. 处理负数值 ["-12,020000" -> "-12.02"]
  5. 去除多个点号 ["-12.0.20.000" -> "-12.02"]
  6. 无需额外库,只需使用标准的Java

就是这样...

public class NumUtils {
    /**
     * Transforms a string to an integer. If no numerical chars returns a String "0".
     *
     * @param str
     * @return retStr
     */
    static String makeToInteger(String str) {
        String s = str;
        double d;
        d = Double.parseDouble(makeToDouble(s));
        int i = (int) (d + 0.5D);
        String retStr = String.valueOf(i);
        System.out.printf(retStr + "   ");
        return retStr;
    }

    /**
     * Transforms a string to an double. If no numerical chars returns a String "0".
     *
     * @param str
     * @return retStr
     */
    static String makeToDouble(String str) {

        Boolean dotWasFound = false;
        String orgStr = str;
        String retStr;
        int firstDotPos = 0;
        Boolean negative = false;

        //check if str is null
        if(str.length()==0){
            str="0";
        }

        //check if first sign is "-"
        if (str.charAt(0) == '-') {
            negative = true;
        }

        //check if str containg any number or else set the string to '0'
        if (!str.matches(".*\\d+.*")) {
            str = "0";
        }

        //Replace ',' with '.'  (for some european users who use the ',' as decimal separator)
        str = str.replaceAll(",", ".");
        str = str.replaceAll("[^\\d.]", "");

        //Removes the any second dots
        for (int i_char = 0; i_char < str.length(); i_char++) {
            if (str.charAt(i_char) == '.') {
                dotWasFound = true;
                firstDotPos = i_char;
                break;
            }
        }
        if (dotWasFound) {
            String befDot = str.substring(0, firstDotPos + 1);
            String aftDot = str.substring(firstDotPos + 1, str.length());
            aftDot = aftDot.replaceAll("\\.", "");
            str = befDot + aftDot;
        }

        //Removes zeros from the begining
        double uglyMethod = Double.parseDouble(str);
        str = String.valueOf(uglyMethod);

        //Removes the .0
        str = str.replaceAll("([0-9])\\.0+([^0-9]|$)", "$1$2");

        retStr = str;

        if (negative) {
            retStr = "-"+retStr;
        }

        return retStr;

    }

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

}

5

异常处理代价高昂,但在这种情况下,正则表达式需要更长的时间。以下代码展示了两个函数的简单测试 - 一个使用异常处理,另一个使用正则表达式。 在我的机器上,正则表达式版本比异常处理版本慢10倍。

import java.util.Date;


public class IsNumeric {

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

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

public static void main(String [] args) {

    String test = "12345.F";

    long before = new Date().getTime();     
    for(int x=0;x<1000000;++x) {
        //isNumericTwo(test);
        isNumericOne(test);
    }
    long after = new Date().getTime();

    System.out.println(after-before);

}

}

一般来说,我认为这种代码会用于检查输入的类型。在这种情况下,速度并不是考虑因素,使用抛出异常来检查数字或非数字等丑陋的方法是错误的。 - user872985
也许不是。通常,输入的类型由UI组件检查,可以在提交值之前立即显示错误。更常见的可能是从大型输入文本文件中验证字符串-其中性能很重要。我在这里的答案的目标是解决接受的答案中“异常很慢”的说法。复杂的正则表达式要昂贵得多。而我的代码中根本没有“难看的抛出”-只有一种更快的检测违规的方法。通过先检查再计算的方法,您需要通过输入进行两次传递:一次用于验证,另一次用于转换。 - ChrisCantrell
在我的电脑上,正则表达式版本比异常版本慢10倍。这只是因为您测试的值是数字,所以从未抛出异常。在非数字值上进行测试,带有异常的版本将比正则表达式版本慢。 - Ancient Behemoth
很棒的一点。我以为在末尾添加“F”会使其不是数字,但Java的“parseDouble”喜欢它。我被纠正了。 - ChrisCantrell

5

// 请查看以下代码

public static boolean isDigitsOnly(CharSequence str) {
    final int len = str.length();
    for (int i = 0; i < len; i++) {
        if (!Character.isDigit(str.charAt(i))) {
            return false;
        }
    }
    return true;
}

问题中提到“数字”,这可能包括非整数值。 - rghome

4

我已经说明了一些条件,可以在不使用任何API的情况下检查数字和小数,

检查固定长度为1位的数字

Character.isDigit(char)

检查固定长度数字(假设长度为6)

String number = "132452";
if(number.matches("([0-9]{6})"))
System.out.println("6 digits number identified");

检查长度在4到6之间的变长数字

//  {n,m}  n <= length <= m
String number = "132452";
if(number.matches("([0-9]{4,6})"))
System.out.println("Number Identified between 4 to 6 length");

String number = "132";
if(!number.matches("([0-9]{4,6})"))
System.out.println("Number not in length range or different format");

检查变长小数,长度在4到7之间。
//  It will not count the '.' (Period) in length
String decimal = "132.45";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "1.12";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "1234";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "-10.123";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "123..4";
if(!decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Decimal not in range or different format");

String decimal = "132";
if(!decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Decimal not in range or different format");

String decimal = "1.1";
if(!decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Decimal not in range or different format");

希望这能帮助到大家。

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