Самый быстрый способ проверить, может ли String быть разобран на Double in Java

Я знаю там миллион способов сделать это, но что самое быстрое? Это должно включать научную нотацию.

ПРИМЕЧАНИЕ. Меня не интересует преобразование значения в Double, мне интересно только знать, возможно ли это. т.е. private boolean isDouble(String value).

Ответ 1

Вы можете проверить его, используя то же регулярное выражение, которое использует 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
        }

Ответ 2

В a > . Это немного странно:

Допустимые числа включают шестнадцатеричные, отмеченные с помощью классификатора 0x, научной нотации и числа, помеченные квалификатором типа (например, 123L).

но я предполагаю, что это может быть быстрее, чем регулярные выражения, или бросать и ловить исключение.

Ответ 3

Apache Commons NumberUtil на самом деле довольно быстро. Я предполагаю, что это быстрее, чем любой regexp.

Ответ 4

Я использую следующий код, чтобы проверить, может ли строка быть разобрана в 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();
}

Ответ 5

Я думаю, что попытка конвертировать его в двойную, и перехват исключения будет самым быстрым способом проверить... другой способ, о котором я могу думать, - расщепить строку на период ('.'), а затем проверить, что каждая часть массива split содержит только целые числа... но я думаю, что первый способ был бы быстрее

Ответ 6

Я попытался использовать блок кода и, похоже, быстрее выбрал исключение

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

Ответ 7

Исключения не должны использоваться для управления потоком, хотя авторы Java затруднили использование NumberFormatException таким образом.

Класс java.util.Scanner имеет метод hasNextDouble, чтобы проверить, можно ли считать String как double.

Под капотом Scanner используются регулярные выражения (через предварительно скомпилированные шаблоны), чтобы определить, можно ли преобразовать String в число целых чисел или чисел с плавающей запятой. Шаблоны скомпилированы в методе buildFloatAndDecimalPattern, который вы можете просмотреть в GrepCode здесь.

Предварительно скомпилированный шаблон имеет дополнительное преимущество: он быстрее, чем использование блока 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);
}