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)