Самый элегантный способ определить, является ли String числом?

Есть ли лучший, более элегантный (и / или, возможно, более быстрый) способ, чем

boolean isNumber = false;
try{
   Double.valueOf(myNumber);
   isNumber = true;
} catch (NumberFormatException e) {
}

...?


Редактировать : Поскольку я не могу выбрать два ответа, я использую регулярное выражение, потому что а) это элегантно и б) говорить "Джон Скит решил проблему" - это тавтология, потому что сам Джон Скит является решением всех проблем.

11.12.2008 14:56:44
Именно так я и делал, когда пользовательский ввод должен быть числом. Однако никогда не сталкивался с чем-либо еще.
Chris Serra 11.12.2008 14:58:39
Я задал очень похожий вопрос некоторое время назад. Некоторые из этих ответов могут помочь. stackoverflow.com/questions/237159/…
Bill the Lizard 11.12.2008 16:03:49
Jajajajaja хороший ответ Epaga.
Agusti-N 11.12.2008 19:15:24
11 ОТВЕТОВ
РЕШЕНИЕ

Я не верю, что в Java есть что-то встроенное, чтобы делать это быстрее и надежнее, предполагая, что позже вы захотите проанализировать это с помощью Double.valueOf (или аналогичного).

Я бы использовал Double.parseDouble вместо Double.valueOf, чтобы избежать ненужного создания Double, и вы также можете избавиться от явно глупых чисел быстрее, чем исключение, проверив цифры, e / E, - и. заранее. Итак, что-то вроде:

public boolean isDouble(String value)
{        
    boolean seenDot = false;
    boolean seenExp = false;
    boolean justSeenExp = false;
    boolean seenDigit = false;
    for (int i=0; i < value.length(); i++)
    {
        char c = value.charAt(i);
        if (c >= '0' && c <= '9')
        {
            seenDigit = true;
            continue;
        }
        if ((c == '-' || c=='+') && (i == 0 || justSeenExp))
        {
            continue;
        }
        if (c == '.' && !seenDot)
        {
            seenDot = true;
            continue;
        }
        justSeenExp = false;
        if ((c == 'e' || c == 'E') && !seenExp)
        {
            seenExp = true;
            justSeenExp = true;
            continue;
        }
        return false;
    }
    if (!seenDigit)
    {
        return false;
    }
    try
    {
        Double.parseDouble(value);
        return true;
    }
    catch (NumberFormatException e)
    {
        return false;
    }
}

Обратите внимание, что, несмотря на несколько попыток, это все равно не охватывает «NaN» или шестнадцатеричные значения. Хотите ли вы, чтобы те прошли или нет, зависит от контекста.

По моему опыту регулярные выражения медленнее, чем жестко заданная проверка выше.

10
12.12.2008 16:22:08
По моему опыту, я обычно хочу Double и знать, является ли он допустимым двойным или нет, поэтому я, вероятно, изменил бы ваш метод, чтобы он возвращал Double или генерировал исключение NumberFormatException.
Paul Tomblin 11.12.2008 15:13:42
... или вернуть крошечный класс с булевыми и двойными числами, или использовать двойной предел, в зависимости от необходимости.
Paul Tomblin 11.12.2008 15:14:26
@ Пол: Действительно. К этому моменту вы в основном реализовали .NET "bool Double.TryParse (текст строки, из двойника). В Java альтернативой было бы возвращать Double (объект) или ноль для" недопустимого ".
Jon Skeet 11.12.2008 15:18:20
Pssst ... + 1.0e-7 - действительный дубль.
plinth 11.12.2008 15:41:42
Пссст ... все равно не получится. Есть два возможных знака в двойном: до мантиссы и после е / Е, и знак может быть «+». Здесь на самом деле есть урок, заключающийся в том, что спецификация для double более сложна, чем ваш код: лучше сделать, если ("0123456789eE - +". Contains (c)) continue;
plinth 11.12.2008 15:49:20

Вы можете использовать регулярное выражение, то есть что-то вроде String.matches("^[\\d\\-\\.]+$");(если вы не проверяете отрицательные числа или числа с плавающей запятой, вы можете немного упростить).

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

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

О моих результатах вы можете прочитать в моем блоге . (Подсказка: Джон Скит FTW).

9
15.12.2008 11:48:17
Это также потерпит неудачу для отрицательных чисел и нецелых чисел :)
Jon Skeet 11.12.2008 15:04:24
Кто-нибудь проверял, чтобы увидеть, является ли регулярное выражение быстрее или медленнее, чем метод Джона Скита ниже? Регулярное выражение, безусловно, выглядит чище, если вы найдете тот, который работает для ожидаемых вами чисел.
Paul Tomblin 11.12.2008 15:15:44
Некоторые люди, сталкиваясь с проблемой, думают: «Я знаю, я буду использовать регулярные выражения». Теперь у них две проблемы. ;)
Doctor Jones 11.12.2008 15:26:41
@Paul: я делал тесты с .NET давным-давно, и регулярные выражения были значительно медленнее. Если вы хотите использовать регулярное выражение, хотя бы один раз его скомпилируйте и скомпилируйте :) (хотя я подозреваю, что он все равно будет работать медленнее. Кстати, мой код теперь охватывает несколько случаев больше, чем он был изначально, и он начинает усложняться. ..)
Jon Skeet 11.12.2008 15:47:36
и этот ответ, и предположение Джона Скита о том, что десятичный разделитель есть. и вы не разрешите тысячи разделителей.
John Gardner 11.12.2008 18:59:56

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

2
11.12.2008 15:01:53
commons-lang также обнаруживает вещи, которых нет в Java, например: множественные запятые, пробелы вокруг числа и некоторые другие.
Aaron Digulla 11.12.2008 15:04:36
Это зависит от того, что вы хотите сделать. Есть числа, которые не могут быть представлены как двойники Java - должны они быть включены или нет? Если вы действительно хотите знать, сможете ли вы в конечном итоге конвертировать в Java double, я подозреваю, что вам будет трудно сделать это надежно без вызова Double.
Jon Skeet 11.12.2008 15:07:04

Используйте StringUtils.isDouble(String)в Apache Commons.

3
11.12.2008 15:17:22
Перемещено в более новых версиях. См. Org.apache.commons.lang3.math.NumberUtils. В частности NumberUtils.isParseable () и NumberUtils.isCreatable ().
David Leppik 7.04.2017 16:26:56

После ответа Филла я могу предложить еще одно регулярное выражение?

String.matches("^-?\\d+(\\.\\d+)?$");
1
11.12.2008 15:14:42

Большинство из этих ответов являются несколько приемлемыми решениями. У всех решений regex есть проблема неправильности во всех случаях, которые могут вас волновать.

Если вы действительно хотите убедиться, что String является действительным числом, я бы использовал ваше собственное решение. Не забывайте об этом, я полагаю, что в большинстве случаев String будет действительным числом и не вызовет исключения. Таким образом, большую часть времени производительность будет идентична производительности Double.valueOf ().

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

похотливый

2
11.12.2008 15:21:42
Мне нравится подход регулярных выражений. Хотя это правда, что представленный материал может не соответствовать требованиям ОП, это просто потому, что требования расплывчаты. Представить способ разработки решения - это лучшее, что мы можем сделать.
Brian Knoblauch 11.12.2008 15:31:36
Я понимаю, что вы говорите. Если цель состоит в том, чтобы просто обнаружить цифры, то будет работать [0-9] +.
Randy Stegbauer 16.12.2008 14:46:10

См. Java.text.NumberFormat (javadoc).

NumberFormat nf = NumberFormat.getInstance(Locale.FRENCH);
Number myNumber = nf.parse(myString);
int myInt = myNumber.intValue();
double myDouble = myNumber.doubleValue();
8
11.12.2008 16:01:52
Приятно видеть ответ, который не предполагает языковых допущений. +1
J c 12.12.2008 16:21:00
«Глобальное сообщество» - вы должны иметь правильное форматирование / синтаксический анализ локали. 1,024,00 в Германии - 1 024,00 в Париже и 1024,00 в Нью-Йорке.
Ran Biron 14.12.2008 06:58:46

Усиление от мистера Скита:

private boolean IsValidDoubleChar(char c)
{
    return "0123456789.+-eE".indexOf(c) >= 0;
}

public boolean isDouble(String value)
{
    for (int i=0; i < value.length(); i++)
    {
        char c = value.charAt(i);
        if (IsValidDoubleChar(c))
            continue;
        return false;
    }
    try
    {
        Double.parseDouble(value);
        return true;
    }
    catch (NumberFormatException e)
    {
        return false;
    }
}
3
11.12.2008 16:05:15
Попытка сделать это для 1 меня ложна, когда это должно быть правдой: P
test 20.08.2014 04:44:41

Правильное регулярное выражение на самом деле дано в двойных javadocs :

Чтобы не вызывать этот метод для недопустимой строки и вызывать исключение 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
    }

Это не учитывает локализованные представления, однако:

Чтобы интерпретировать локализованные строковые представления значения с плавающей точкой, используйте подклассы NumberFormat .

5
12.12.2008 16:23:56

Если вам нужно что-то невероятно быстрое и у вас есть четкое представление о том, какие форматы вы хотите принять, вы можете создать конечный автомат DFA вручную. По сути, так или иначе работают регулярные выражения, но вы можете избежать этапа компиляции регулярных выражений таким образом, и он может быть быстрее, чем универсальный компилятор регулярных выражений.

-1
12.12.2008 16:24:19
Хотя это теоретически интересный ответ, он не очень полезен, поскольку вы не предоставляете информацию о том, как сконструировать такую ​​машину и использовать ее в контексте.
opticyclic 10.04.2013 19:33:24

Я предпочитаю использовать цикл над представлением Strings char [] и использовать метод Character.isDigit (). Если элегантность желательна, я думаю, что это наиболее читабельно :

package tias;

public class Main {
  private static final String NUMERIC = "123456789";
  private static final String NOT_NUMERIC = "1L5C";

  public static void main(String[] args) {
    System.out.println(isStringNumeric(NUMERIC));
    System.out.println(isStringNumeric(NOT_NUMERIC));
  }

  private static boolean isStringNumeric(String aString) {
    if (aString == null || aString.length() == 0) {
      return false;
    }
    for (char c : aString.toCharArray() ) {
      if (!Character.isDigit(c)) {
        return false;
      }
    }
    return true;
  }

}

1
15.12.2008 13:53:16