Наилучшая реализация для метода isNumber (string)

В моем ограниченном опыте я работал над несколькими проектами, у которых был какой-то класс класса строк с методами, чтобы определить, является ли данная строка числом. Идея всегда была одинаковой, однако реализация была другой. Некоторые окружают попытку разбора с помощью try/catch

public boolean isInteger(String str) {
    try {
        Integer.parseInt(str);
        return true;
    } catch (NumberFormatException nfe) {}
    return false;
}

и другие соответствуют регулярному выражению

public boolean isInteger(String str) {
    return str.matches("^-?[0-9]+(\\.[0-9]+)?$");
}

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

Примечание. Поскольку я новичок на сайте, я не совсем понимаю этот бизнес сообщества Wiki, поэтому, если это относится к нему, дайте мне знать, и я с радостью переведу его.

EDIT: Со всеми предложениями TryParse я поместил код сравнения Asaph (спасибо за отличный пост!) На С# и добавил метод TryParse. И, как кажется, TryParse выигрывает руки. Тем не менее, подход try catch занял безумное количество времени. Насколько я думаю, я сделал что-то не так! Я также обновил регулярное выражение для обработки отрицательных и десятичных точек.

Результаты для обновленного кода С#:

00:00:51.7390000 for isIntegerParseInt
00:00:03.9110000 for isIntegerRegex
00:00:00.3500000 for isIntegerTryParse

Использование:

static bool isIntegerParseInt(string str) {
    try {
        int.Parse(str);
        return true;
    } catch (FormatException e){}
    return false;
}

static bool isIntegerRegex(string str) {
    return Regex.Match(str, "^-?[0-9]+(\\.[0-9]+)?$").Success;
}

static bool isIntegerTryParse(string str) {
    int bob;
    return Int32.TryParse(str, out bob);
}

Ответ 1

Я только что запустил некоторые тесты производительности этих двух методов (на Macbook Pro OSX Leopard Java 6). ParseInt работает быстрее. Вот результат:

This operation took 1562 ms.
This operation took 2251 ms.

И вот мой тестовый код:


public class IsIntegerPerformanceTest {

    public static boolean isIntegerParseInt(String str) {
        try {
            Integer.parseInt(str);
            return true;
        } catch (NumberFormatException nfe) {}
        return false;
    }

    public static boolean isIntegerRegex(String str) {
        return str.matches("^[0-9]+$");
    }

    public static void main(String[] args) {
        long starttime, endtime;
        int iterations = 1000000;
        starttime = System.currentTimeMillis();
        for (int i=0; i<iterations; i++) {
            isIntegerParseInt("123");
            isIntegerParseInt("not an int");
            isIntegerParseInt("-321");
        }
        endtime = System.currentTimeMillis();
        System.out.println("This operation took " + (endtime - starttime) + " ms.");
        starttime = System.currentTimeMillis();
        for (int i=0; i<iterations; i++) {
            isIntegerRegex("123");
            isIntegerRegex("not an int");
            isIntegerRegex("-321");
        }
        endtime = System.currentTimeMillis();
        System.out.println("This operation took " + (endtime - starttime) + " ms.");
    }
}

Также обратите внимание, что ваше регулярное выражение будет отклонять отрицательные числа, и метод parseInt примет их.

Ответ 2

Вот наш способ сделать это:

public boolean isNumeric(String string) throws IllegalArgumentException
{
   boolean isnumeric = false;

   if (string != null && !string.equals(""))
   {
      isnumeric = true;
      char chars[] = string.toCharArray();

      for(int d = 0; d < chars.length; d++)
      {
         isnumeric &= Character.isDigit(chars[d]);

         if(!isnumeric)
         break;
      }
   }
   return isnumeric;
}

Ответ 3

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

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

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

Ответ 4

Мне нужно было реорганизовать код как ваш, чтобы избавиться от NumberFormatException. Реорганизованный код:

public static Integer parseInteger(final String str) {
    if (str == null || str.isEmpty()) {
        return null;
    }
    final Scanner sc = new Scanner(str);
    return Integer.valueOf(sc.nextInt());
}

Как парень Java 1.4, я не знал о java.util.Scanner. Я нашел эту интересную статью:

http://rosettacode.org/wiki/Determine_if_a_string_is_numeric#Java

Мне понравилось решение со сканером, очень компактное и по-прежнему читаемое.

Ответ 5

Некоторые языки, такие как С#, имеют TryParse (или эквивалент), который работает достаточно хорошо для чего-то вроде этого.

public boolean IsInteger(string value)
{
  int i;
  return Int32.TryParse(value, i);
}

Ответ 6

Лично я сделал бы это, если вы действительно хотите его упростить.

public boolean isInteger(string myValue)
{
    int myIntValue;
    return int.TryParse(myValue, myIntValue)
}

Ответ 7

Вы можете создать метод расширения для строки и сделать весь процесс более чистым...

public static bool IsInt(this string str)
{
    int i;
    return int.TryParse(str, out i);
}

Затем вы можете сделать следующее в своем фактическом коде...

if(myString.IsInt())....

Ответ 8

Используя .NET, вы можете сделать что-то вроде:

private bool isNumber(string str)
{
    return str.Any(c => !char.IsDigit(c));
}

Ответ 9

public static boolean CheckString(String myString) {

char[] digits;

    digits = myString.toCharArray();
    for (char div : digits) {// for each element div of type char in the digits collection (digits is a collection containing div elements).
        try {
            Double.parseDouble(myString);
            System.out.println("All are numbers");
            return true;
        } catch (NumberFormatException e) {

            if (Character.isDigit(div)) {
                System.out.println("Not all are chars");

                return false;
            }
        }
    }

    System.out.println("All are chars");
    return true;
}

Ответ 10

Это моя реализация, чтобы проверить, состоит ли строка из цифр:

public static boolean isNumeric(String string)
{
    if (string == null)
    {
        throw new NullPointerException("The string must not be null!");
    }
    final int len = string.length();
    if (len == 0)
    {
        return false;
    }
    for (int i = 0; i < len; ++i)
    {
        if (!Character.isDigit(string.charAt(i)))
        {
            return false;
        }
    }
    return true;
}

Ответ 11

Мне нравится код:

public static boolean isIntegerRegex(String str) {
    return str.matches("^[0-9]+$");
}

Но при создании шаблона это будет еще больше:

public static Pattern patternInteger = Pattern.compile("^[0-9]+$");
public static boolean isIntegerRegex(String str) {
  return patternInteger.matcher(str).matches();
}

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

This operation isIntegerParseInt took 1313 ms.
This operation isIntegerRegex took 1178 ms.
This operation isIntegerRegexNew took 304 ms.

С

public class IsIntegerPerformanceTest {
  private static Pattern pattern = Pattern.compile("^[0-9]+$");

    public static boolean isIntegerParseInt(String str) {
    try {
      Integer.parseInt(str);
      return true;
    } catch (NumberFormatException nfe) {
    }
    return false;
  }

  public static boolean isIntegerRegexNew(String str) {
    return pattern.matcher(str).matches();
  }

  public static boolean isIntegerRegex(String str) {
    return str.matches("^[0-9]+$");
  }

    public static void main(String[] args) {
        long starttime, endtime;
    int iterations = 1000000;
    starttime = System.currentTimeMillis();
    for (int i = 0; i < iterations; i++) {
      isIntegerParseInt("123");
      isIntegerParseInt("not an int");
      isIntegerParseInt("-321");
    }
    endtime = System.currentTimeMillis();
    System.out.println("This operation isIntegerParseInt took " + (endtime - starttime) + " ms.");
    starttime = System.currentTimeMillis();
    for (int i = 0; i < iterations; i++) {
      isIntegerRegex("123");
      isIntegerRegex("not an int");
      isIntegerRegex("-321");
    }
    endtime = System.currentTimeMillis();
    System.out.println("This operation took isIntegerRegex " + (endtime - starttime) + " ms.");
    starttime = System.currentTimeMillis();
    for (int i = 0; i < iterations; i++) {
      isIntegerRegexNew("123");
      isIntegerRegexNew("not an int");
      isIntegerRegexNew("-321");
    }
    endtime = System.currentTimeMillis();
    System.out.println("This operation took isIntegerRegexNew " + (endtime - starttime) + " ms.");
  }
}

Ответ 12

Я думаю, что это может быть быстрее предыдущих решений, если вы выполните следующее (Java):

public final static boolean isInteger(String in)
{
    char c;
    int length = in.length();
    boolean ret = length > 0;
    int i = ret && in.charAt(0) == '-' ? 1 : 0;
    for (; ret && i < length; i++)
    {
        c = in.charAt(i);
        ret = (c >= '0' && c <= '9');
    }
    return ret;
}

Я запустил тот же код, что и Асаф, и результат:

Эта операция заняла 28 мс.

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

Ответ 13

Я думаю, что людям здесь не хватает точки. Использование одного и того же шаблона неоднократно имеет очень легкую оптимизацию. Просто используйте одиночный шаблон. Выполняя это, во всех моих тестах подход try-catch никогда не имеет лучшего ориентира, чем шаблонный подход. При успешном тестировании try-catch занимает в два раза больше времени, а тест сбоя - в 6 раз медленнее.

public static final Pattern INT_PATTERN= Pattern.compile("^-?[0-9]+(\\.[0-9]+)?$");

public static boolean isInt(String s){
  return INT_PATTERN.matcher(s).matches();
}

Ответ 14

Я использую это, но мне нравилась строгость Асафа на его посту.

public static bool IsNumeric(object expression)
{
if (expression == null)
return false;

double number;
return Double.TryParse(Convert.ToString(expression, CultureInfo.InvariantCulture),   NumberStyles.Any,
NumberFormatInfo.InvariantInfo, out number);
}

Ответ 15

Для длинных номеров используйте это: (JAVA)

public static boolean isNumber(String string) {
    try {
        Long.parseLong(string);
    } catch (Exception e) {
        return false;
    }
    return true;
}

Ответ 16

 public static boolean isNumber(String str){
      return str.matches("[0-9]*\\.[0-9]+");
    }

чтобы проверить, является ли число (включая float, integer) или нет

Ответ 17

Измененная версия моего предыдущего ответа:

public static boolean isInteger(String in)
{
    if (in != null)
    {
        char c;
        int i = 0;
        int l = in.length();
        if (l > 0 && in.charAt(0) == '-')
        {
            i = 1;
        }
        if (l > i)
        {
            for (; i < l; i++)
            {
                c = in.charAt(i);
                if (c < '0' || c > '9')
                    return false;
            }
            return true;
        }
    }
    return false;
}

Ответ 18

Я просто добавил этот класс к моим утилитам:

public class TryParseLong {
private boolean isParseable;

private long value;

public TryParseLong(String toParse) {
    try {
        value = Long.parseLong(toParse);
        isParseable = true;
    } catch (NumberFormatException e) {
        // Exception set to null to indicate it is deliberately
        // being ignored, since the compensating action
        // of clearing the parsable flag is being taken.
        e = null;

        isParseable = false;
    }
}

public boolean isParsable() {
    return isParseable;
}

public long getLong() {
    return value;
}
}

Чтобы использовать его:

TryParseLong valueAsLong = new TryParseLong(value);

if (valueAsLong.isParsable()) {
    ...
    // Do something with valueAsLong.getLong();
} else {
    ...
}

Это только одно раз анализирует значение.

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

Проблема с Java по сравнению с С# заключается в том, что С# имеет значения и передает по ссылке, поэтому может эффективно возвращать 2 части информации; флаг, указывающий, что что-то разбирается или нет, и фактическое анализируемое значение. Когда мы reutrn > 1 значение в Java, нам нужно создать объект для их хранения, поэтому я взял этот подход и поместил флаг и проанализированное значение в объект.

Анализ Escape, скорее всего, справится с этим эффективно и создаст значение и флаг в стеке и никогда не создаст этот объект в куче, поэтому я думаю, что это будет иметь минимальное влияние на производительность.

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

Ответ 19

public static boolean CheckIfNumber (номер строки) {

    for(int i = 0; i < number.length(); i++){
        try{
            Double.parseDouble(number.substring(i));

        }catch(NumberFormatException ex){
            return false;
        }
    }
    return true;     
}

У меня была эта проблема раньше, но когда я вводил число, а затем символ, он все равно вернул бы true, я думаю, что это лучший способ сделать это. Просто проверьте, есть ли каждый char номер. Немного дольше, но он заботится, если у вас есть ситуация, когда пользователь вводит "1abc". По какой-то причине, когда я пытался попытаться поймать без итерации, он все еще считал, что это число так.