String to Int в java - Вероятно, плохие данные, нужно избегать исключений

Увидев, что Java не имеет типов с нулевым значением и не имеет TryParse(), как вы обрабатываете входную проверку без исключения исключений?

Обычный способ:

String userdata = /*value from gui*/
int val;
try
{
   val = Integer.parseInt(userdata);
}
catch (NumberFormatException nfe)
{
   // bad data - set to sentinel
   val = Integer.MIN_VALUE;
}

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

Какая наилучшая практика для решения этой ситуации?

EDIT: Обоснование: Было много разговоров о SO об обработке исключений, и общая позиция заключается в том, что исключения должны использоваться только для неожиданных сценариев. Тем не менее, я думаю, что неправильный ввод пользователя ОЖИДАЕТ, а не редко. Да, это действительно академическая точка.

Дальнейшие изменения:

Некоторые ответы показывают, что не так с SO. Вы игнорируете задаваемый вопрос и отвечаете на другой вопрос, который не имеет к этому никакого отношения. Вопрос не в том, чтобы переходить между слоями. Вопрос не в том, что нужно вернуть, если число несовместимо. Насколько вам известно, val = Integer.MIN_VALUE; является в точности правильной опцией для приложения, из которого извлекается этот полностью контекстный фрагмент кода.

Ответ 1

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

Возможно также полезно (в зависимости от приложения) регистрировать плохой ввод, чтобы вы могли отслеживать.

Ответ 2

Я спросил если были библиотеки с открытым исходным кодом, у которых были методы для этого разбора, и ответ да!

Из Apache Commons Lang вы можете использовать NumberUtils.toInt:

// returns defaultValue if the string cannot be parsed.
int i = org.apache.commons.lang.math.NumberUtils.toInt(s, defaultValue);

Из Google Guava вы можете использовать Ints.tryParse:

// returns null if the string cannot be parsed
// Will throw a NullPointerException if the string is null
Integer i = com.google.common.primitives.Ints.tryParse(s);

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

Ответ 3

Для данных, предоставленных пользователем, Integer.parseInt обычно является неправильным методом, поскольку он не поддерживает интернационализацию. Пакет java.text - ваш (многословный) друг.

try {
    NumberFormat format = NumberFormat.getIntegerInstance(locale);
    format.setParseIntegerOnly(true);
    format.setMaximumIntegerDigits(9);
    ParsePosition pos = new ParsePosition(0);
    int val = format.parse(str, pos).intValue();
    if (pos.getIndex() != str.length()) {
        // ... handle case of extraneous characters after digits ...
    }
    // ... use val ...
} catch (java.text.ParseFormatException exc) {
    // ... handle this case appropriately ...
}

Ответ 4

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

Ответ 5

Я уверен, что это плохая форма, но у меня есть набор статических методов в классе Utilities, которые делают такие вещи, как Utilities.tryParseInt(String value), который возвращает 0, если String не поддается анализу и Utilities.tryParseInt(String value, int defaultValue), который позволяет вам указать значение для использования, если parseInt() генерирует исключение.

Я считаю, что времена, когда возвращение известного значения на плохом вводе вполне приемлемо. Очень надуманный пример: вы запрашиваете у пользователя дату в формате YYYYMMDD, и они дают вам плохую информацию. Вполне возможно, что в зависимости от требований программы можно сделать что-то вроде Utilities.tryParseInt(date, 19000101) или Utilities.tryParseInt(date, 29991231);.

Ответ 6

Я собираюсь повторить то, что stinkyminky делал в нижней части сообщения:

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

Как только вы узнаете, что вы правильно подтвердили ввод пользователя, тогда его можно легко проанализировать и проигнорировать, зарегистрировать или преобразовать в RuntimeException исключение NumberFormatException.

Обратите внимание: этот подход требует, чтобы вы рассматривали свою модель в двух частях: бизнес-модели (где мы действительно заботимся о наличии значений в формате int или float) и модели пользовательского интерфейса (где мы действительно хотим разрешить пользователю ставить в том, что они хотят).

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

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

Связывающие библиотеки, такие как JGoodies Binding и JSR 295, делают эту вещь намного проще реализовать, чем может показаться, - и многие веб-фреймворки предоставляют конструкции, которые отделяют пользователя от фактической бизнес-модели, заполняя только бизнес-объекты после завершения проверки.

С точки зрения проверки файлов конфигурации (другой вариант использования, представленный в некоторых комментариях), одно дело указывать значение по умолчанию, если конкретное значение вообще не указано, но если данные отформатированы неправильно (кто-то типы "oh" вместо "нуля" - или они скопировали из MS Word, и все обратные тики получили фанковый символ юникода), тогда нужна какая-то системная обратная связь (даже если она просто не работает с приложением, бросая исключение времени выполнения).

Ответ 7

Вот как я это делаю:

public Integer parseInt(String data) {
  Integer val = null;
  try {
    val = Integer.parseInt(userdata);
  } catch (NumberFormatException nfe) { }
  return val;
}

Затем нулевые сигналы недействительны. Если вы хотите установить значение по умолчанию, вы можете изменить его на:

public Integer parseInt(String data,int default) {
  Integer val = default;
  try {
    val = Integer.parseInt(userdata);
  } catch (NumberFormatException nfe) { }
  return val;
}

Ответ 8

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

Я бы не стал использовать альтернативу regex из-за накладных расходов.

Ответ 9

Попробуйте org.apache.commons.lang.math.NumberUtils.createInteger(String s). Это очень помогло мне. Существуют аналогичные методы для удвоений, длин и т.д.

Ответ 10

Вы можете использовать Integer, для которого может быть установлено значение null, если у вас плохое значение. Если вы используете java 1.6, он предоставит вам автоматический бокс/распаковку.

Ответ 11

Java 8 "Нет значения" Семантика

В Java 8+ я теперь рассмотрел бы использование RegEx для предварительного фильтра (чтобы избежать исключения, как вы отметили) и обертывания результата в примитиве необязательно (для решения проблемы по умолчанию):

public static OptionalInt toInt(final String input) {
    return input.matches("[+-]?\\d+") 
            ? OptionalInt.of(Integer.parseInt(input)) 
            : OptionalInt.empty();
}

flatMap() Поддержка

Если вы хотите использовать это с помощью flatMap(), используйте эквивалентный тип потока:

public static IntStream toInt(final String input) {
    return input.matches("[+-]?\\d+") 
            ? IntStream.of(Integer.parseInt(input)) 
            : IntStream.empty();
}

Что вы можете использовать следующим образом:

inputs.flapMapToInt(MyUtility::toInt);

Refs

RegEx на основе документация parseInt

Ответ 12

Вышеприведенный код плохой, потому что он эквивалентен следующему.

// this is bad
int val = Integer.MIN_VALUE;
try
{
   val = Integer.parseInt(userdata);
}
catch (NumberFormatException ignoreException) { }

Исключение полностью игнорируется. Кроме того, волшебный токен плох, потому что пользователь может пройти в -2147483648 (Integer.MIN_VALUE).

Общий вопрос, основанный на анализе, не выгоден. Скорее, это должно иметь отношение к контексту. У вашего приложения есть особые требования. Вы можете определить свой метод как

private boolean isUserValueAcceptable(String userData)
{
   return (    isNumber(userData)    
          &&   isInteger(userData)   
          &&   isBetween(userData, Integer.MIN_VALUE, Integer.MAX_VALUE ) 
          );
}

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

Ответ 13

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

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

Ответ 14

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

Лично я чувствую, что вы выбрали один из примеров, где исключения действительно ценны. Это распространенная проблема, когда пользователь вводит неправильное значение, и обычно вам нужно вернуться к пользователю для правильного значения. Обычно вы не возвращаетесь к значению по умолчанию, если вы спрашиваете пользователя; что дает пользователю впечатление, которое имеет значение для его ввода.

Если вы не хотите иметь дело с исключением, просто оберните его в RuntimeException (или производном классе), и он позволит вам игнорировать исключение в вашем коде (и убить ваше приложение, когда оно происходит, и это слишком хорошо).

Некоторые примеры того, как я буду обрабатывать исключения NumberFormat: В данных конфигурации веб-приложения:

loadCertainProperty(String propVal) {
  try
  {
    val = Integer.parseInt(userdata);
    return val;
  }
  catch (NumberFormatException nfe)
  { // RuntimeException need not be declared
    throw new RuntimeException("Property certainProperty in your configuration is expected to be " +
                               " an integer, but was '" + propVal + "'. Please correct your " +
                               "configuration and start again");
    // After starting an enterprise application the sysadmin should always check availability
    // and can now correct the property value
  }
}

В графическом интерфейсе:

public int askValue() {
  // TODO add opt-out button; see Swing docs for standard dialog handling
  boolean valueOk = false;
  while(!valueOk) {
    try {
      String val = dialog("Please enter integer value for FOO");
      val = Integer.parseInt(userdata);
      return val; 
    } catch (NumberFormatException nfe) {
      // Ignoring this; I don't care how many typo the customer makes
    }
  }
}

В веб-форме: верните форму пользователю с полезным сообщением об ошибке и верный. Большинство фреймворков предлагают стандартизованный способ проверки.

Ответ 15

Integer.MIN_VALUE как NumberFormatException - плохая идея.

Вы можете добавить предложение в Project Coin, чтобы добавить этот метод в Integer

@Nullable public static Integer parseInteger (String src)... он вернет null для плохого ввода

Затем поместите ссылку на ваше предложение здесь, и все мы проголосуем за него!

PS: Посмотрите на это http://msdn.microsoft.com/en-us/library/bb397679.aspx это как уродливое и раздутое это могло быть

Ответ 16

Поместите некоторые операторы if перед ним. if (null!= userdata)