Comments 41
Напиши один раз — отлаживай везде…
Разбирался как-то с реализацией parseDouble() в OpenJDK. Код на удивление запутанный и объемный. Зато корректный, наверное.
Как сказать…
Вот, например, до сих пор неисправленный: JDK-8043740
Еще один относительно свежий: JDK-7019078
Ну и, конечно, печально знаменитая уязвимость.
Вот, например, до сих пор неисправленный: JDK-8043740
Еще один относительно свежий: JDK-7019078
Ну и, конечно, печально знаменитая уязвимость.
Я согласен, что метод работает не совсем корректно. Однако, использовать parseDouble для проверки формата входящих данных и построение логики на основе Exceptions может быть не самой лучшей идеей.
В общем-то, я с вами согласен. В свою защиту могу сказать, что код был написан до того момента, как он перешел ко мне. К счастью, случай с данным багом заставил меня поменять реализацию.
И как вы теперь проверяете если это не число?
Я нашел баг под конец рабочего дня, поэтому временно заменил его таким решением, не знаю насколько оно производительно:
Другие варианты:
— написать regexp;
— использовать NumberUtils.isNumber() из Apache Commons Lang
ParsePosition parsePosition = new ParsePosition(0);
NumberFormat.getInstance().parse(stringToCheck, parsePosition);
boolean isNumber = parsePosition.getIndex() == stringToCheck.length();
Другие варианты:
— написать regexp;
— использовать NumberUtils.isNumber() из Apache Commons Lang
А пройтись в for по каждой букве и вызвать
Если хоть один false выходим и сразу возвращаем false.
Character.isDigit(буква);
не пробывали?Если хоть один false выходим и сразу возвращаем false.
Ну вообще-то, эту самую «e» никто не отменял. Причем она должна встретиться не более одного раза. А после нее возможен знак. И в начале числа еще возможен знак. А еще точка может быть. Причем только одна, причем до буквы «e». А еще, я, возможно, что-то упустил или забыл… Что-то, что учли разработчики
Я, кстати, сам большой ветеран велосипедостроения. ;)
NumberUtils.isNumber()
.Я, кстати, сам большой ветеран велосипедостроения. ;)
Примерно так:
public class Validation {
public static boolean isNumeric(String value) {
for (Character c : value.toCharArray()){
if (!Character.isDigit(c)) {
return false;
}
}
return true;
}
}
Если все-таки решите остановиться на регулярках, то не торопитесь ломать голову — заграница нам поможет!
А действительно, почему это не хорошая идея? Если распарсилось — значит число, что нам и нужно (за исключением таких багов, конечно).
Логика за эксепшенах — медленно и сложнее читается.
Exception-ы изначально разрабатывались для того, чтобы дать приложению шанс не рухнуть и не потечь в случае каких-либо совсем неожиданностей — ну типа там аппаратный сбой, или там out of memory, или какой-нибудь database gone, которые могут приключиться в совершенно любом месте кода. Соответственно, реализуются они так, чтобы [почти] не добавлять накладных расходов в том случае, если эти самые исключения не происходят. Ну, т.е., никто не проверяет после вызова каждой функции, а не вылетело ли OutOfMemoryException вместо нормально результата. Вместо этого, если исключение таки выброшено, вызывается довольно мудрёный обработчик, который крутит стэк, прибивает недопроинициализированные объекты, которые ещё нельзя убивать штатным образом, и делает прочую чёрную магию. Но — за дорого.
Поэтому считается, что использовать исключения там, где вы ожидаете вероятные ошибки, нельзя — типа, слишком дорого по ресурсам.
С другой стороны, сегодня в большинстве софта основной источник «ожидаемых» ошибок — ввод пользователя, а там экономить на спичках смысла нет.
Поэтому концепция правильного использования исключений стала забываться.
Поэтому считается, что использовать исключения там, где вы ожидаете вероятные ошибки, нельзя — типа, слишком дорого по ресурсам.
С другой стороны, сегодня в большинстве софта основной источник «ожидаемых» ошибок — ввод пользователя, а там экономить на спичках смысла нет.
Поэтому концепция правильного использования исключений стала забываться.
не только питон, и не только GUI
blogs.msdn.com/b/oldnewthing/archive/2004/12/15/313250.aspx
… Intel representatives asked, «So if you could ask for only one thing to be made faster, what would it be?»
Without hesitation, one of the Microsoft lead kernel developers replied, «Speed up faulting on an invalid instruction.»
It so happens that on the 80386 chip of that era, the fastest way to get from V86-mode into kernel mode was to execute an invalid instruction! Consequently, Windows/386 used an invalid instruction as its syscall trap.
blogs.msdn.com/b/oldnewthing/archive/2004/12/15/313250.aspx
… Intel representatives asked, «So if you could ask for only one thing to be made faster, what would it be?»
Without hesitation, one of the Microsoft lead kernel developers replied, «Speed up faulting on an invalid instruction.»
It so happens that on the 80386 chip of that era, the fastest way to get from V86-mode into kernel mode was to execute an invalid instruction! Consequently, Windows/386 used an invalid instruction as its syscall trap.
Я не знаю как там в Java, но exception это такое состояние программы, когда она просто не знает что делать дальше. Совершенно очевидно, что если парсеру double подсунули строку «преведмедвед», то возникает именно такая ситуация и совершенно правильно, что он выкидывает исключение. А какие ещё у него пути об этом сообщить? То что у Java тяжелые эксепшены, это не проблема исключений как инструмента. Выше правильно заметили, что на питоне писать try перед любым обработчиком это хорошо и правильно, не говоря об Erlang, например, где логика только на этом и строится.
А мне кажется, что всё зависит от ситуации. Само слово exception указывает на то, что речь идет об исключительной ситуации. В Java бывают 2 типа исключительных ситуаций — ситуации «непреодолимой силы» (то есть unhandled) и ситуации, которые можно (и нужно) обработать. Мы сейчас обсуждаем вторые. И они как раз и предназначены для того, чтобы строить на них логику. Но, конечно, не в любом случае.
1. Если мы берем нечто и оно с вероятностью 50% либо окажется числом, либо нет, то, конечно, тут
2. Но если мы, скажем, читаем колонку чисел, среди которых может случиться ошибка, то
В целом, думаю, правило должно выглядеть так: исключения целесообразны в том случае, если
1. некоторый класс ситуаций маловероятен и его можно назвать «нештатным»
2. если несколько сходных ситуаций обрабатываются в обход основного алгоритма и сходным образом
1. Если мы берем нечто и оно с вероятностью 50% либо окажется числом, либо нет, то, конечно, тут
parseDouble
и ловля исключения — не лучшая идея.2. Но если мы, скажем, читаем колонку чисел, среди которых может случиться ошибка, то
exception
— лучшее решение.В целом, думаю, правило должно выглядеть так: исключения целесообразны в том случае, если
1. некоторый класс ситуаций маловероятен и его можно назвать «нештатным»
2. если несколько сходных ситуаций обрабатываются в обход основного алгоритма и сходным образом
ИМХО, если уж и кидать этот NumberFormatException, то он должен быть checked. Я, например, ожидаю, что если не распарсилось — то возвращается 0, как в каком-нибудь JS — это вполне логично, но кидать целое исключение, которое с достаточно большой вероятностью может уронить всё приложение, всего лишь из-за того, что не удалось распарсить число — это реально перебор.
Да, пусть лучше приложение продолжит работу со значением из ниоткуда (а если это банковское приложение?)
Приложение, с которым непосредственно взаимодействует пользователь (а на андроиде это в 99% случаев так), должно продолжать работу в любом случае, что бы ни происходило, потому что пользователю очень неприятно, когда у него что-то вылетает. Если оно банковское — так ведь с моим вариантом проверить правильность введённых значений ещё проще. В конце концов, отсутствие unchecked-исключений, выкидываемых по мелочам там, где их ждёшь меньше всего, делает код проще.
Вы не обижайтесь, но у вас джава скрипт головного мозга. Возвращать 0 когда число не распарсилось это жесть — почему не 100, или 146?
А зачем парсить UUID?
Так получилось. На вход приходят различные объекты в виде строк, нужно понять число это или нет.
Была у меня как-то похожая надобность. Так вот, в процессе поиска информации, встречались «умельцы», разбрающие проходящую строку посимвольно, с целью определения — число это или нет…
Преданья старины глубокой... Ребята еще в 2011-м сдались. Предлагаю вам реанимировать этот bug report.
Очень похожая история — поновее и на русском. Там тоже люди считали, что считывание байтов с CD — освоенная технология.
Баг месяц назад был вроде пофикшен в AOSP android-review.googlesource.com/#/c/102376/
Sign up to leave a comment.
Баг Double.parseDouble() в Android