Convert.ToInt32 против TryParse - PullRequest
5 голосов
/ 27 мая 2009

Мы все знаем, какое влияние может оказать множество исключений на производительность наших приложений, поэтому мы должны избегать таких вещей, как использование исключений для потока управления. После этого заявления я должен признаться, что при кодировании меня это не особо волновало. Я работал в основном на платформе Java, но в последнее время я делал это на платформе .NET и только что нашел этот удобный метод: public static bool TryParse(string s,out int result) , который позволяет вам преобразовать String в int без исключения. С этого момента я продолжаю использовать его. Я просто хотел спросить вас о ваших предпочтениях относительно использования public static bool TryParse(string s,out int result) или public static int ToInt32(string value).

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

boolean isInteger = Pattern.matches("^\d*$", myString);

Спасибо.

Ответы [ 5 ]

7 голосов
/ 27 мая 2009

Да, в Java отсутствует аналогичный метод, хотя без параметров out это на самом деле довольно сложно выразить (хотя и нужно вернуть примитив). Как правило, однако, в C # вы должны использовать TryParse, если вы ожидаете, что значение иногда не будет целым числом, и ToInt32 в противном случае; таким образом, «исключительная» ситуация рассматривается как таковая.

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

3 голосов
/ 27 мая 2009

Я не знаю о C #, но в Java исключения только дороги, когда они на самом деле выбрасываются, но тогда они действительно очень дороги. Если вы ожидаете, что значительная часть строк будет недействительной, стоит сначала проверить их, даже если вы используете регулярное выражение.

Но не используйте String.matches() или Pattern.matches() для применения регулярного выражения; эти методы перекомпилируют регулярное выражение каждый раз, когда вы вызываете их. Вместо этого заранее скомпилируйте регулярное выражение и сохраните его как объект Pattern, а затем выполните проверку с этим. В моих тестах при разборе списка из 10000 строк, из которых 20% были недействительными, предварительная проверка с использованием шаблона выполняется почти в два раза быстрее, чем при использовании только Integer.parseInt() и перехвата исключений.

Однако это обсуждение применимо только в том случае, если вы выполняете много преобразований в тесном цикле. Если вы делаете их только время от времени, например, когда принимаете пользовательский ввод, разрешите Integer.parseInt() выполнить проверку, это нормально. И если вы решите проверить с помощью регулярного выражения, вам понадобится намного лучшее регулярное выражение, чем ^\d*$ - это регулярное выражение будет соответствовать пустой строке, а также "числам", большим Integer.MAX_VALUE, и не будет соответствовать отрицательному цифры на всех.

1 голос
/ 11 декабря 2010

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

boolean isInteger = Pattern.matches("^\d*$", myString);

Чтобы предсказать, если Integer.parseInt(myString) выдаст исключение, есть еще много работы. Строка может начинаться с -. Также int не может иметь более 10 значащих цифр. Таким образом, более надежное выражение будет ^-?0*\d{1,10}$. Но даже это выражение не может предсказать каждое исключение, потому что оно все еще слишком неточно.

Сформировать надежное регулярное выражение можно. Но это было бы очень долго. Также возможно реализовать метод, который точно определяет, будет ли parseInt генерировать исключение. Это может выглядеть так:

static boolean wouldParseIntThrowException(String s) {
    if (s == null || s.length() == 0) {
        return true;
    }

    char[] max = Integer.toString(Integer.MAX_VALUE).toCharArray();
    int i = 0, j = 0, len = s.length();
    boolean maybeOutOfBounds = true;

    if (s.charAt(0) == '-') {
        if (len == 1) {
            return true; // s == "-"
        }
        i = 1;
        max[max.length - 1]++; // 2147483647 -> 2147483648
    }
    while (i < len && s.charAt(i) == '0') {
        i++;
    }
    if (max.length < len - i) {
        return true; // too long / out of bounds
    } else if (len - i < max.length) {
        maybeOutOfBounds = false;
    }
    while (i < len) {
        char digit = s.charAt(i++);
        if (digit < '0' || '9' < digit) {
            return true;
        } else if (maybeOutOfBounds) {
            char maxdigit = max[j++];
            if (maxdigit < digit) {
                return true; // out of bounds
            } else if (digit < maxdigit) {
                maybeOutOfBounds = false;
            }
        }
    }
    return false;
}

Я не знаю, какая версия более эффективна. И это в основном зависит от контекста, какие проверки являются разумными.

В C # к проверьте , если строка может быть преобразована, вы бы использовали TryParse. И если он возвращает true, то в качестве побочного продукта он получает , преобразованный в то же время. Это полезная функция, и я не вижу проблемы с простым переопределением parseInt для возврата null вместо выдачи исключения.

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

private static Pattern QUITE_ACCURATE_INT_PATTERN = Pattern.compile("^-?0*\\d{1,10}$");

static Integer tryParseIntegerWhichProbablyResultsInOverflow(String s) {
    Integer result = null;
    if (!wouldParseIntThrowException(s)) {
        try {
            result = Integer.parseInt(s);
        } catch (NumberFormatException ignored) {
            // never happens
        }
    }
    return result;
}

static Integer tryParseIntegerWhichIsMostLikelyNotEvenNumeric(String s) {
    Integer result = null;
    if (s != null && s.length() > 0 && QUITE_ACCURATE_INT_PATTERN.matcher(s).find()) {
        try {
            result = Integer.parseInt(s);
        } catch (NumberFormatException ignored) {
        // only happens if the number is too big
        }
    }
    return result;
}

static Integer tryParseInteger(String s) {
    Integer result = null;
    if (s != null && s.length() > 0) {
        try {
            result = Integer.parseInt(s);
        } catch (NumberFormatException ignored) {
        }
    }
    return result;
}

static Integer tryParseIntegerWithoutAnyChecks(String s) {
    try {
        return Integer.parseInt(s);
    } catch (NumberFormatException ignored) {
    }
    return null;
}
1 голос
/ 27 мая 2009

Для этой цели в Java вы можете использовать хорошо известный StringUtils (на общем языке), у этого класса есть метод isNumeric

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

public static boolean isNumeric(String str) {
  if (str == null) {
    return false;
  }
  int sz = str.length();
  for (int i = 0; i < sz; i++) {
    if (Character.isDigit(str.charAt(i)) == false) {
      return false;
    }
  }
  return true;
 }

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

0 голосов
/ 27 мая 2009

Я просто хотел спросить вас о ваших предпочтениях относительно использования общедоступного статического bool TryParse (строка s, результат out int) или публичного статического int ToInt32 (значение строки).

Да, я использую TryParse, за исключением случаев, когда я ожидаю, что значение всегда будет действительным. Я считаю, что он читается чище, чем с использованием исключений. Даже если я хочу исключение, я обычно хочу настроить сообщение или выдать свое собственное исключение; следовательно, я использую TryParse и вручную генерирую исключение.

В Java и C # я пытаюсь поймать минимальный набор возможных исключений. В Java это означает, что я должен отдельно отлавливать NullPointerException и NumberFormatException в ответ на Number.ValueOf (...); альтернативно, я могу поймать «Исключение» и рискнуть поймать что-то непреднамеренное. С TryParse в C # я не беспокоюсь об этом вообще.

...