Как проверить, является ли строка числовой в Java - PullRequest
799 голосов
/ 09 июля 2009

Как бы вы проверили, была ли строка строкой, прежде чем ее проанализировать?

Ответы [ 39 ]

852 голосов
/ 09 июля 2009

Как правило, это делается с помощью простой пользовательской функции (т. Е. Roll-your-own «isNumeric»).

Что-то вроде:

public static boolean isNumeric(String str) { 
  try {  
    Double.parseDouble(str);  
    return true;
  } catch(NumberFormatException e){  
    return false;  
  }  
}

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

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

public static boolean isNumeric(String str) {
  return str.matches("-?\\d+(\\.\\d+)?");  //match a number with optional '-' and decimal.
}

Будьте осторожны с вышеупомянутым механизмом RegEx, так как он потерпит неудачу, если вы используете неарабские цифры (то есть цифры, отличные от 0 до 9). Это связано с тем, что часть "\ d" в RegEx будет совпадать только с [0-9] и фактически не будет численно известна на международном уровне. (Спасибо OregonGhost за указание на это!)

Или даже другой альтернативой является использование встроенного в Java объекта java.text.NumberFormat, чтобы проверить, находится ли после синтаксического анализа строки позиция синтаксического анализатора в конце строки. Если это так, мы можем предположить, что вся строка числовая:

public static boolean isNumeric(String str) {
  NumberFormat formatter = NumberFormat.getInstance();
  ParsePosition pos = new ParsePosition(0);
  formatter.parse(str, pos);
  return str.length() == pos.getIndex();
}
633 голосов
/ 24 сентября 2012

С Apache Commons Lang 3,5 и выше: NumberUtils.isCreatable или StringUtils.isNumeric.

С Apache Commons Lang 3.4 и ниже: NumberUtils.isNumber или StringUtils.isNumeric.

Вы также можете использовать StringUtils.isNumericSpace, который возвращает true для пустых строк и игнорирует внутренние пробелы в строке. Другой способ - использовать StringUtils.isParsable, который в основном проверяет, является ли число доступным для анализа в соответствии с Java. (Связанные javadoc содержат подробные примеры для каждого метода.)

141 голосов
/ 23 ноября 2013

если вы на андроиде, то вам следует использовать:

android.text.TextUtils.isDigitsOnly(CharSequence str)

документацию можно найти здесь

будь проще . в основном каждый может «перепрограммировать» (тоже самое).

116 голосов
/ 17 августа 2011

Как @CraigTP упомянул в своем превосходном ответе, у меня также есть похожие проблемы с производительностью при использовании Исключений для проверки, является ли строка числовой или нет.В итоге я разбил строку и использовал java.lang.Character.isDigit().

public static boolean isNumeric(String str)
{
    for (char c : str.toCharArray())
    {
        if (!Character.isDigit(c)) return false;
    }
    return true;
}

. В соответствии с Javadoc , Character.isDigit(char) будет правильно распознавать нелатинские цифры.С точки зрения производительности, я думаю, что простое число сравнений N, где N - количество символов в строке, было бы более вычислительно эффективным, чем сопоставление с регулярным выражением.комментарий, приведенный выше код будет проверять только положительные целые числа, что покрывает большую часть моего варианта использования.Ниже приведен обновленный код, который корректно проверяет десятичные числа в соответствии с локалью по умолчанию, используемой в вашей системе, при условии, что десятичный разделитель встречается в строке только один раз.

public static boolean isStringNumeric( String str )
{
    DecimalFormatSymbols currentLocaleSymbols = DecimalFormatSymbols.getInstance();
    char localeMinusSign = currentLocaleSymbols.getMinusSign();

    if ( !Character.isDigit( str.charAt( 0 ) ) && str.charAt( 0 ) != localeMinusSign ) return false;

    boolean isDecimalSeparatorFound = false;
    char localeDecimalSeparator = currentLocaleSymbols.getDecimalSeparator();

    for ( char c : str.substring( 1 ).toCharArray() )
    {
        if ( !Character.isDigit( c ) )
        {
            if ( c == localeDecimalSeparator && !isDecimalSeparatorFound )
            {
                isDecimalSeparatorFound = true;
                continue;
            }
            return false;
        }
    }
    return true;
}
91 голосов
/ 13 декабря 2015

лямбда-выражения Java 8.

String someString = "123123";
boolean isNumeric = someString.chars().allMatch( Character::isDigit );
43 голосов
/ 30 января 2012

Библиотека Google Guava предоставляет хороший вспомогательный метод для этого: Ints.tryParse. Вы используете его как Integer.parseInt, но он возвращает null вместо того, чтобы выдавать исключение, если строка не анализирует действительное целое число. Обратите внимание, что он возвращает Integer, а не int, поэтому вам нужно конвертировать / autobox обратно в int.

Пример:

String s1 = "22";
String s2 = "22.2";
Integer oInt1 = Ints.tryParse(s1);
Integer oInt2 = Ints.tryParse(s2);

int i1 = -1;
if (oInt1 != null) {
    i1 = oInt1.intValue();
}
int i2 = -1;
if (oInt2 != null) {
    i2 = oInt2.intValue();
}

System.out.println(i1);  // prints 22
System.out.println(i2);  // prints -1

Однако в текущем выпуске - Guava r11 - он все еще помечен @Beta.

Я не тестировал это. Глядя на исходный код, есть некоторые издержки из-за проверки работоспособности, но в конце они используют Character.digit(string.charAt(idx)), похожий, но немного отличный от ответа из @Ibrahim выше. В их реализации нет накладных расходов на обработку исключений под покрытиями.

27 голосов
/ 31 августа 2015

Не используйте исключения для проверки ваших значений. Вместо этого используйте Util libs как apache NumberUtils:

NumberUtils.isNumber(myStringValue);

Редактировать

Обратите внимание, что если ваша строка начинается с 0, NumberUtils интерпретирует ваше значение как шестнадцатеричное.

NumberUtils.isNumber("07") //true
NumberUtils.isNumber("08") //false
21 голосов
/ 29 марта 2015

Почему все стремятся к решениям исключений / регулярных выражений?

Хотя я понимаю, что большинству людей хорошо использовать try / catch, но если вы хотите делать это часто ... это может быть очень сложным.

Что я сделал здесь, так это возьмем регулярное выражение, методы parseNumber () и метод поиска в массиве, чтобы увидеть, какой из них наиболее эффективен. На этот раз я посмотрел только на целые числа.

public static boolean isNumericRegex(String str) {
    if (str == null)
        return false;
    return str.matches("-?\\d+");
}

public static boolean isNumericArray(String str) {
    if (str == null)
        return false;
    char[] data = str.toCharArray();
    if (data.length <= 0)
        return false;
    int index = 0;
    if (data[0] == '-' && data.length > 1)
        index = 1;
    for (; index < data.length; index++) {
        if (data[index] < '0' || data[index] > '9') // Character.isDigit() can go here too.
            return false;
    }
    return true;
}

public static boolean isNumericException(String str) {
    if (str == null)
        return false;
    try {  
        /* int i = */ Integer.parseInt(str);
    } catch (NumberFormatException nfe) {  
        return false;  
    }
    return true;
}

Результаты по скорости, которые я получил, были:

Done with: for (int i = 0; i < 10000000; i++)...

With only valid numbers ("59815833" and "-59815833"):
    Array numeric took 395.808192 ms [39.5808192 ns each]
    Regex took 2609.262595 ms [260.9262595 ns each]
    Exception numeric took 428.050207 ms [42.8050207 ns each]
    // Negative sign
    Array numeric took 355.788273 ms [35.5788273 ns each]
    Regex took 2746.278466 ms [274.6278466 ns each]
    Exception numeric took 518.989902 ms [51.8989902 ns each]
    // Single value ("1")
    Array numeric took 317.861267 ms [31.7861267 ns each]
    Regex took 2505.313201 ms [250.5313201 ns each]
    Exception numeric took 239.956955 ms [23.9956955 ns each]
    // With Character.isDigit()
    Array numeric took 400.734616 ms [40.0734616 ns each]
    Regex took 2663.052417 ms [266.3052417 ns each]
    Exception numeric took 401.235906 ms [40.1235906 ns each]

With invalid characters ("5981a5833" and "a"):
    Array numeric took 343.205793 ms [34.3205793 ns each]
    Regex took 2608.739933 ms [260.8739933 ns each]
    Exception numeric took 7317.201775 ms [731.7201775 ns each]
    // With a single character ("a")
    Array numeric took 291.695519 ms [29.1695519 ns each]
    Regex took 2287.25378 ms [228.725378 ns each]
    Exception numeric took 7095.969481 ms [709.5969481 ns each]

With null:
    Array numeric took 214.663834 ms [21.4663834 ns each]
    Regex took 201.395992 ms [20.1395992 ns each]
    Exception numeric took 233.049327 ms [23.3049327 ns each]
    Exception numeric took 6603.669427 ms [660.3669427 ns each] if there is no if/null check

Отказ от ответственности: я не утверждаю, что эти методы оптимизированы на 100%, они просто для демонстрации данных

Исключения выигрывают тогда и только тогда, когда число составляет 4 символа или менее, а каждая строка всегда число ... в таком случае, зачем вообще чек?

Короче говоря, это очень больно, если вы часто сталкиваетесь с недопустимыми числами с помощью try / catch, что имеет смысл. Важное правило, которому я всегда следую, это НИКОГДА не использовать try / catch для выполнения программы . Это пример почему.

Интересно, что простой if char <0 || > 9 было чрезвычайно просто написать, легко запомнить (и должно работать на нескольких языках) и выигрывает почти во всех тестовых сценариях.

Единственным недостатком является то, что я предполагаю, что Integer.parseInt () может обрабатывать числа не ASCII, в то время как метод поиска по массиву не делает.


Для тех, кто задается вопросом, почему я сказал, что легко запомнить массив символов, если вы знаете, что нет отрицательных признаков, вы можете легко сойтись с чем-то сжатым, как это:

public static boolean isNumericArray(String str) {
    if (str == null)
        return false;
    for (char c : str.toCharArray())
        if (c < '0' || c > '9')
            return false;
    return true;

И, наконец, в заключение, мне было любопытно, что оператор присваивания в принятом примере набрал все голоса. Добавление в присвоение

double d = Double.parseDouble(...)

не только бесполезен, поскольку вы даже не используете это значение, но он тратит время обработки и увеличивает время выполнения на несколько наносекунд (что привело к увеличению времени тестирования на 100-200 мс). Я не понимаю, почему кто-то так поступил, потому что это дополнительная работа по снижению производительности.

Можно подумать, что это будет оптимизировано ... хотя, возможно, мне следует проверить байт-код и посмотреть, что делает компилятор. Это не объясняет, почему он всегда показывался мне более длинным, хотя, если он каким-то образом оптимизирован ... поэтому мне интересно, что происходит. Примечание: под продолжительностью я подразумеваю запуск теста для 10000000 итераций, и запуск этой программы несколько раз (10x +) всегда показывал, что он медленнее.

РЕДАКТИРОВАТЬ: Обновлен тест для Character.isDigit ()

18 голосов
/ 17 сентября 2011
public static boolean isNumeric(String str)
{
    return str.matches("-?\\d+(.\\d+)?");
}

Регулярное выражение CraigTP (показано выше) дает некоторые ложные срабатывания. Например. «23y4» будет считаться числом, потому что '.' соответствует любому символу, кроме десятичной точки.

Также он отклонит любое число с начальным '+'

Альтернатива, которая позволяет избежать этих двух незначительных проблем:

public static boolean isNumeric(String str)
{
    return str.matches("[+-]?\\d*(\\.\\d+)?");
}
12 голосов
/ 09 июля 2009

Вы можете использовать NumberFormat#parse:

try
{
     NumberFormat.getInstance().parse(value);
}
catch(ParseException e)
{
    // Not a number.
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...