Как узнать, является ли значение, содержащееся в строке, двойным или нет - PullRequest
23 голосов
/ 28 июня 2010

В Java я пытаюсь выяснить, является ли значение, содержащееся в строке, двойным или нет?

Ответы [ 13 ]

31 голосов
/ 28 июня 2010
    boolean isDouble(String str) {
        try {
            Double.parseDouble(str);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }
13 голосов
/ 28 июня 2010

В источнике есть примечание для Double:

[...] Чтобы избежать вызова этого метода для недопустимой строки и выброса NumberFormatException, регулярное выражение ниже можно использовать для проверки входной строки: [...]

Окончательная форма следующих регулярных выражений довольно длинная:

[\x00-\x20]*[+-]?(NaN|Infinity|((((\p{Digit}+)(\.)?((\p{Digit}+)?)([eE][+-]?(\p{Digit}+))?)|(\.((\p{Digit}+))([eE][+-]?(\p{Digit}+))?)|(((0[xX](\p{XDigit}+)(\.)?)|(0[xX](\p{XDigit}+)?(\.)(\p{XDigit}+)))[pP][+-]?(\p{Digit}+)))[fFdD]?))[\x00-\x20]*

Однако, используя этот метод, вы можете легко исключить некоторые специальные двойные числа, такие как Infinity и NaN, которые оба принимаются Double.parseDouble. Например, как это:

String regExp = "[\\x00-\\x20]*[+-]?(((((\\p{Digit}+)(\\.)?((\\p{Digit}+)?)([eE][+-]?(\\p{Digit}+))?)|(\\.((\\p{Digit}+))([eE][+-]?(\\p{Digit}+))?)|(((0[xX](\\p{XDigit}+)(\\.)?)|(0[xX](\\p{XDigit}+)?(\\.)(\\p{XDigit}+)))[pP][+-]?(\\p{Digit}+)))[fFdD]?))[\\x00-\\x20]*";
boolean matches = yourString.matches(regExp);
7 голосов
/ 28 июня 2010

Использование Scanner будет значительно медленнее, чем использование Double.parseDouble(String s).

private static Random rand = new Random();
private static final String regExp = "[\\x00-\\x20]*[+-]?(((((\\p{Digit}+)(\\.)?((\\p{Digit}+)?)([eE][+-]?(\\p{Digit}+))?)|(\\.((\\p{Digit}+))([eE][+-]?(\\p{Digit}+))?)|(((0[xX](\\p{XDigit}+)(\\.)?)|(0[xX](\\p{XDigit}+)?(\\.)(\\p{XDigit}+)))[pP][+-]?(\\p{Digit}+)))[fFdD]?))[\\x00-\\x20]*";
private static final Pattern pattern = Pattern.compile(regExp);

public static void main(String[] args) {

    int trials = 50000;
    String[] values = new String[trials];

    // initialize the array
    // about half the values will be parsable as double
    for( int i = 0; i < trials; ++i ) {
        double d = rand.nextDouble();
        boolean b = rand.nextBoolean();

        values[i] = (b ? "" : "abc") + d;
    }

    long start = System.currentTimeMillis();

    int parseCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleParse(values[i]) ) {
            parseCount++;
        }
    }

    long end = System.currentTimeMillis();
    long elapsed = end - start;

    System.out.println("Elapsed time parsing: " + elapsed + " ms");
    System.out.println("Doubles: " + parseCount);

    // reset the timer for the next run
    start = System.currentTimeMillis();

    int scanCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleScan(values[i]) ) {
            scanCount++;
        }
    }

    end = System.currentTimeMillis();
    elapsed = end - start;

    System.out.println("Elapsed time scanning: " + elapsed + " ms");
    System.out.println("Doubles: " + scanCount);


    // reset the timer for the next run
    start = System.currentTimeMillis();

    int regexCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleRegex(values[i]) ) {
            regexCount++;
        }
    }

    end = System.currentTimeMillis();
    elapsed = end - start;

    System.out.println("Elapsed time regex (naive): " + elapsed + " ms");
    System.out.println("Doubles: " + naiveRegexCount);


    // reset the timer for the next run
    start = System.currentTimeMillis();

    int compiledRegexCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleCompiledRegex(values[i]) ) {
            compiledRegexCount++;
        }
    }

    end = System.currentTimeMillis();
    elapsed = end - start;

    System.out.println("Elapsed time regex (compiled): " + elapsed + " ms");
    System.out.println("Doubles: " + compiledRegexCount);
}


public static boolean isDoubleParse(String s) {
    if( s == null ) return false;
    try {
        Double.parseDouble(s);
        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}

public static boolean isDoubleScan(String s) {
    Scanner scanner = new Scanner(s);
    return scanner.hasNextDouble();
}

public static boolean isDoubleRegex(String s) {
    return s.matches(regExp);
}

public static boolean isDoubleCompiledRegex(String s) {
    Matcher m = pattern.matcher(s);
    return m.matches();
}

Когда я запускаю код выше, я получаю следующий вывод:

Время анализа: 235 мс
Двухместный номер: 24966
Время сканирования: 31358 мс
Двухместный номер: 24966
Регулярное выражение истекшего времени (наивное): 1829 мс
Двухместный номер: 24966
Регулярное выражение истекшего времени (скомпилировано): 109 мс
Двухместный номер: 24966

Метод регулярного выражения выполняется довольно быстро, учитывая сложность регулярного выражения, но все же не так быстро, как простой синтаксический анализ с использованием Double.parseDouble(s). Как указано в комментариях, есть несколько значений, таких как NaN, которые проходят через анализатор, что, вероятно, не должно.

Обновление:

Предварительная компиляция регулярного выражения в соответствии с предложением @ Гейб имеет все значение. Скомпилированный метод регулярных выражений теперь явный победитель.

7 голосов
/ 28 июня 2010

Вы можете создать Scanner(String) и использовать метод hasNextDouble(). Из своего javadoc:

Возвращает true, если следующий токен на входе этого сканера может быть интерпретируется как двойное значение с использованием метод nextDouble(). Сканер не продвигается дальше любого ввода.

Например, этот фрагмент:

List<String> values = Arrays.asList("foo", "1", "2.3", "1f", "0.2d", "3.14");
for (String source : values) {
    Scanner scanner = new Scanner(source);
    System.out.println(String.format("%4s: %s", source, scanner.hasNextDouble()));
}

выдаст следующий вывод:

 foo: false
   1: true
 2.3: true
  1f: false
0.2d: false
3.14: true
6 голосов
/ 28 июня 2010
public boolean isDouble(String value) {
    try {
        Double.parseDouble(value);
        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}
5 голосов
/ 10 февраля 2015

Вы можете использовать класс утилит из Apache Commons Lang:

NumberUtils.isNumber(aString);

Это абсолютно безопасно и не требует использования блока try-catch.

Примечание: для анализа парных чисел, работает, если десятичный разделитель является точкой .

Edit: isNumber устарел и будет удален из Lang 4.0

Лучше использовать:

NumberUtils.isCreatable(aString);
2 голосов
/ 28 июня 2010

Другие предполагают, что вы также можете знать, что вход НЕ выражается как целое число.В зависимости от ваших требований, это может сделать работу быстро и грязно:

public static void main(String[] args) throws Exception {
    System.out.println(isNonIntegerDouble("12"));  //false
    System.out.println(isNonIntegerDouble("12.1")); //true
    System.out.println(isNonIntegerDouble("12.0")); //true
}

public static boolean isNonIntegerDouble(String in) {
    try {
        Double.parseDouble(in);
    } catch (NumberFormatException nfe) {
        return false;
    }
    try {
        new BigInteger(in);
    } catch (NumberFormatException nfe) {
        return true;
    }
    return false;
}

На данный момент, я думаю, что сравнение строк будет более подходящим выбором.

2 голосов
/ 28 июня 2010

Я бы предложил это:

try {
  d = Double.parseDouble(myString);
}
catch (NumberFormatException ex) {
    // Do something smart here...
}
2 голосов
/ 28 июня 2010

Вы можете попытаться разобрать его с помощью Double.parseDouble(String s)

Возвращает удвоение, если разбор был успешным, и исключение, если он не разбирается.

Таким образом, вы можете обернуть все это в функцию, которая содержит try-catch, и вернуть false, если вы получили исключение, или true, если вы получили фактическое значение.

1 голос
/ 28 июня 2010

Вы можете использовать следующее регулярное выражение в строке:

[-+]?[0-9]*\.?[0-9]*

и посмотреть, соответствует ли оно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...