检查String是否可以在Java中解析为Double的最快方法

时间:2011-12-19 17:14:18

标签: java string double

我知道有一百万种方法可以做到这一点,但最快的是什么?这应该包括科学记数法。

注意:我对将值转换为Double不感兴趣,我只想知道它是否可行。即private boolean isDouble(String value)

7 个答案:

答案 0 :(得分:6)

您可以使用Double类使用的相同正则表达式进行检查。这里有很好的记录:

http://docs.oracle.com/javase/6/docs/api/java/lang/Double.html#valueOf%28java.lang.String%29

以下是代码部分:

  

为避免在无效字符串上调用此方法并抛出NumberFormatException,可以使用下面的正则表达式来筛选输入字符串:

  final String Digits     = "(\\p{Digit}+)";
  final String HexDigits  = "(\\p{XDigit}+)";

        // an exponent is 'e' or 'E' followed by an optionally 
        // signed decimal integer.
        final String Exp        = "[eE][+-]?"+Digits;
        final String fpRegex    =
            ("[\\x00-\\x20]*"+  // Optional leading "whitespace"
             "[+-]?(" + // Optional sign character
             "NaN|" +           // "NaN" string
             "Infinity|" +      // "Infinity" string

             // A decimal floating-point string representing a finite positive
             // number without a leading sign has at most five basic pieces:
             // Digits . Digits ExponentPart FloatTypeSuffix
             // 
             // Since this method allows integer-only strings as input
             // in addition to strings of floating-point literals, the
             // two sub-patterns below are simplifications of the grammar
             // productions from the Java Language Specification, 2nd 
             // edition, section 3.10.2.

             // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
             "((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+

             // . Digits ExponentPart_opt FloatTypeSuffix_opt
             "(\\.("+Digits+")("+Exp+")?)|"+

       // Hexadecimal strings
       "((" +
        // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
        "(0[xX]" + HexDigits + "(\\.)?)|" +

        // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
        "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +

        ")[pP][+-]?" + Digits + "))" +
             "[fFdD]?))" +
             "[\\x00-\\x20]*");// Optional trailing "whitespace"

  if (Pattern.matches(fpRegex, myString))
            Double.valueOf(myString); // Will not throw NumberFormatException
        else {
            // Perform suitable alternative action
        }

答案 1 :(得分:5)

NumberUtils#isNumber中有一个方便的Apache Commons Lang。这有点牵强:

  

有效数字包括标有0x限定符的十六进制,科学记数法和用类型限定符标记的数字(例如123L)。

但我想它可能比正则表达式更快或者抛出并捕获异常。

答案 2 :(得分:2)

Apache Commons NumberUtil实际上非常快。我猜它比任何一个都快 regexp实现。

答案 3 :(得分:2)

我使用以下代码检查字符串是否可以解析为double:

public static boolean isDouble(String str) {
    if (str == null) {
        return false;
    }
    int length = str.length();
    if (length == 0) {
        return false;
    }
    int i = 0;
    if (str.charAt(0) == '-') {
        if (length == 1) {
            return false;
        }
        ++i;
    }
    int integerPartSize = 0;
    int exponentPartSize = -1;
    while (i < length) {
        char c = str.charAt(i);
        if (c < '0' || c > '9') {
            if (c == '.' && integerPartSize > 0 && exponentPartSize == -1) {
                exponentPartSize = 0;
            } else {
                return false;
            }
        } else if (exponentPartSize > -1) {
            ++exponentPartSize;
        } else {
            ++integerPartSize;
        }
        ++i;
    }
    if ((str.charAt(0) == '0' && i > 1 && exponentPartSize < 1)
            || exponentPartSize == 0 || (str.charAt(length - 1) == '.')) {
        return false;
    }
    return true;
}

我知道输出与Double类中的正则表达式不完全相同,但这种方法更快,结果足以满足我的需要。这些是我对该方法的单元测试。

@Test
public void shouldReturnTrueIfStringIsDouble() {
    assertThat(Utils.isDouble("0.0")).isTrue();
    assertThat(Utils.isDouble("0.1")).isTrue();
    assertThat(Utils.isDouble("-0.0")).isTrue();
    assertThat(Utils.isDouble("-0.1")).isTrue();
    assertThat(Utils.isDouble("1.0067890")).isTrue();
    assertThat(Utils.isDouble("0")).isTrue();
    assertThat(Utils.isDouble("1")).isTrue();
}

@Test
public void shouldReturnFalseIfStringIsNotDouble() {
    assertThat(Utils.isDouble(".01")).isFalse();
    assertThat(Utils.isDouble("0.1f")).isFalse();
    assertThat(Utils.isDouble("a")).isFalse();
    assertThat(Utils.isDouble("-")).isFalse();
    assertThat(Utils.isDouble("-1.")).isFalse();
    assertThat(Utils.isDouble("-.1")).isFalse();
    assertThat(Utils.isDouble("123.")).isFalse();
    assertThat(Utils.isDouble("1.2.3")).isFalse();
    assertThat(Utils.isDouble("1,3")).isFalse();
}

答案 4 :(得分:0)

我认为尝试将其转换为double并捕获异常将是检查的最快方式...我能想到的另一种方式是将字符串拆分为句点('。')然后检查split数组的每个部分只包含整数......但我认为第一种方式会更快

答案 5 :(得分:0)

我尝试了以下代码块,似乎更快地抛出异常

String a = "123f15512551";
        System.out.println(System.currentTimeMillis());
        a.matches("^\\d+\\.\\d+$");
        System.out.println(System.currentTimeMillis());

        try{
            Double.valueOf(a);
        }catch(Exception e){
            System.out.println(System.currentTimeMillis());
        }

输出:

1324316024735
1324316024737
1324316024737

答案 6 :(得分:0)

异常不应该用于流控制,尽管Java的作者很难不以这种方式使用NumberFormatException

班级java.util.Scanner有一个方法hasNextDouble来检查String是否可以被视为双倍。

引擎盖Scanner使用正则表达式(通过预编译模式)来确定String是否可以转换为整数或浮点数。模式以buildFloatAndDecimalPattern方法编译,您可以在GrepCode here查看。

预编译模式的另一个好处是比使用try / catch块更快。

以下是上面引用的方法,如果GrepCode有一天消失:

private void buildFloatAndDecimalPattern() {
    // \\p{javaDigit} may not be perfect, see above
    String digit = "([0-9]|(\\p{javaDigit}))";
    String exponent = "([eE][+-]?"+digit+"+)?";
    String groupedNumeral = "("+non0Digit+digit+"?"+digit+"?("+
                            groupSeparator+digit+digit+digit+")+)";
    // Once again digit++ is used for performance, as above
    String numeral = "(("+digit+"++)|"+groupedNumeral+")";
    String decimalNumeral = "("+numeral+"|"+numeral +
        decimalSeparator + digit + "*+|"+ decimalSeparator +
        digit + "++)";
    String nonNumber = "(NaN|"+nanString+"|Infinity|"+
                           infinityString+")";
    String positiveFloat = "(" + positivePrefix + decimalNumeral +
                        positiveSuffix + exponent + ")";
    String negativeFloat = "(" + negativePrefix + decimalNumeral +
                        negativeSuffix + exponent + ")";
    String decimal = "(([-+]?" + decimalNumeral + exponent + ")|"+
        positiveFloat + "|" + negativeFloat + ")";
    String hexFloat =
        "[-+]?0[xX][0-9a-fA-F]*\\.[0-9a-fA-F]+([pP][-+]?[0-9]+)?";
    String positiveNonNumber = "(" + positivePrefix + nonNumber +
                        positiveSuffix + ")";
    String negativeNonNumber = "(" + negativePrefix + nonNumber +
                        negativeSuffix + ")";
    String signedNonNumber = "(([-+]?"+nonNumber+")|" +
                             positiveNonNumber + "|" +
                             negativeNonNumber + ")";
    floatPattern = Pattern.compile(decimal + "|" + hexFloat + "|" +
                                   signedNonNumber);
    decimalPattern = Pattern.compile(decimal);
}