Самый быстрый способ проверить, может ли строка анализироваться на Double в Java - PullRequest
11 голосов
/ 19 декабря 2011

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

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

Ответы [ 7 ]

6 голосов
/ 19 декабря 2011

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

Есть удобный NumberUtils#isNumber в Apache Commons Lang .Это немного неправдоподобно:

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

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

2 голосов
/ 10 февраля 2017

Я использую следующий код, чтобы проверить, может ли строка анализироваться на удвоение:

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();
}
2 голосов
/ 02 марта 2012

Apache Commons NumberUtil на самом деле довольно быстрый. Я предполагаю, что это намного быстрее, чем любой Реализация регулярного выражения.

0 голосов
/ 19 декабря 2011

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

В классе java.util.Scanner есть метод hasNextDouble, позволяющий проверить, может ли String читаться как двойное число.

Под капотом 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);
}
0 голосов
/ 19 декабря 2011

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

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
0 голосов
/ 19 декабря 2011

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

...