Как избежать исключения числового формата в java?

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

Затем передайте этот ввод числа на уровень службы или DAO приложения.

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

String cost = request.getParameter("cost");

if (cost !=null && !"".equals(cost) ){
    Integer intCost = Integer.parseInt(cost);
    List<Book> books = bookService . findBooksCheaperThan(intCost);  
}

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

Как лучше всего поступать в таких ситуациях?


person ashishjmeshram    schedule 31.03.2011    source источник
comment
@peter-lawrey: Неверно. Что, если стоимость == ноль? Это совсем не тот чек. Первое выражение вернет false, второе — true.   -  person occulus    schedule 31.03.2011
comment
Что такое исключение NumberFormatException   -  person xenteros    schedule 13.10.2016


Ответы (12)


Просто поймайте свое исключение и выполните правильную обработку исключений:

if (cost !=null && !"".equals(cost) ){
        try {
           Integer intCost = Integer.parseInt(cost);
           List<Book> books = bookService . findBooksCheaperThan(intCost);  
        } catch (NumberFormatException e) {
           System.out.println("This is not a number");
           System.out.println(e.getMessage());
        }
    }
person RoflcoptrException    schedule 31.03.2011
comment
+1: я бы добавил e.getMessage() в сообщение об ошибке. - person Peter Lawrey; 31.03.2011
comment
Да. sysout — это только пример. он все равно должен правильно обрабатывать исключения. Но добавлю. Спасибо. - person RoflcoptrException; 31.03.2011
comment
+1: Наконец-то разумный ответ. Единственное, что я хотел бы изменить, это переместить вызов bookService за пределы блока try, поскольку это конкретное исключение применяется только к синтаксическому анализу строки cost. - person Alexander Pogrebnyak; 31.03.2011
comment
@ Александр Погребняк, да, это правда, но я думаю, это зависит от того, как ОП хочет своей логики. Он не может найти более дешевую книгу без стоимости. (кстати... у тебя то же имя, что и у профессионального футболиста в Бундеслиге :D) - person RoflcoptrException; 31.03.2011
comment
Должны ли мы действительно делать это вместо проверки типа StringUtils.isNumeric(String) перед вызовом parseInt(String)? - person Learner; 22.03.2017

Как всегда, у Jakarta Commons есть хотя бы часть ответа:

NumberUtils.isNumber()

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

person Guillaume    schedule 31.03.2011
comment
Поскольку этот вопрос является самым популярным при поиске в Google NumberFormatException, а ваша ссылка кажется мертвой, вот альтернатива. NumberUtils.isNumber(). В настоящее время он все еще работает, но устарел, поэтому пользуйтесь им с осторожностью. - person cluuu; 21.04.2021

Исключения в последних версиях Java не настолько дороги, чтобы их избегать было важно. Используйте предложенный людьми блок try/catch; если вы поймаете исключение в начале процесса (т. е. сразу после того, как пользователь введет его), тогда у вас не возникнет проблемы позже в процессе (потому что это все равно будет правильный тип).

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

person Joseph Ottinger    schedule 31.03.2011

Я предлагаю сделать 2 вещи:

  • проверить ввод на стороне клиента перед передачей его сервлету
  • поймать исключение и показать сообщение об ошибке в пользовательском интерфейсе, как упоминал Тобиаск. Такого случая обычно не должно происходить, но никогда не доверяйте своим клиентам. ;-)
person Puce    schedule 31.03.2011
comment
ИМХО Проверка на стороне клиента может быть только частью процесса проверки, но вам не на что полагаться (если только вы не хотите открывать окно для всех видов атак)... - person jan groth; 31.03.2011
comment
Вот что я сказал: никогда не доверяйте своим клиентам. Тем не менее, это то, что следует проверить и на стороне клиента. Простая проверка ввода на стороне сервера не очень эффективна. Как уже упоминалось, есть, вероятно, фреймворки/библиотеки, которые могут вам в этом помочь. Может быть, даже новая проверка компонентов (Java EE 6)? (Я еще не тестировал его.) - person Puce; 31.03.2011

одна возможность: поймать исключение и показать сообщение об ошибке в пользовательском интерфейсе.

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

person Tobias    schedule 31.03.2011
comment
Исключения следует использовать в исключительных обстоятельствах, не для обычных ожидаемых ситуаций, например, если ничего не было введено. Исключения дороги в настройке. - person occulus; 31.03.2011
comment
Верно, но пока мы не выводим параметры на Java, вы вряд ли обойдетесь (метод regex неисправен!). Кроме того, почему исключение было бы дорогим в настройке? Это всего лишь запись в таблице исключений, поэтому, пока вы не выбираете исключительный путь кода (а это должно быть редко), реальной проблемы с производительностью не возникает. - person Voo; 31.03.2011
comment
Думаю, Ву прав, как еще можно это проверить? - person Tobias; 31.03.2011
comment
Извините, что вы имеете в виду, вы почти не передвигаетесь? И получить параметры? - person occulus; 31.03.2011
comment
вряд ли обойти [используя исключение] так и должно быть. Для правильного подхода с регулярным выражением потребуется исчерпывающий список чисел (все числа ‹ 10 цифр в порядке, а затем 2000000000|2000000001|..|2147483647). Out параметры были бы, очевидно, лучшим решением, т.е. получить логическое значение подписи tryParseInt(int val, out int erg) - см. С# для одного языка, который имеет это. - person Voo; 31.03.2011

Документация по методу из Apache Commons Lang (отсюда):

Проверяет, является ли String допустимым числом Java.

Допустимые числа включают шестнадцатеричные числа, отмеченные квалификатором 0x, экспоненциальное представление и числа, отмеченные квалификатором типа (например, 123L).

Null и пустая строка вернут false.

Параметры:

`str` - the `String` to check

Возвраты:

`true` if the string is a correctly formatted number

isNumber из java.org.apache.commons.lang3.math.NumberUtils:

public static boolean isNumber(final String str) {
    if (StringUtils.isEmpty(str)) {
        return false;
    }
    final char[] chars = str.toCharArray();
    int sz = chars.length;
    boolean hasExp = false;
    boolean hasDecPoint = false;
    boolean allowSigns = false;
    boolean foundDigit = false;
    // deal with any possible sign up front
    final int start = (chars[0] == '-') ? 1 : 0;
    if (sz > start + 1 && chars[start] == '0' && chars[start + 1] == 'x') {
        int i = start + 2;
        if (i == sz) {
            return false; // str == "0x"
        }
        // checking hex (it can't be anything else)
        for (; i < chars.length; i++) {
            if ((chars[i] < '0' || chars[i] > '9')
                && (chars[i] < 'a' || chars[i] > 'f')
                && (chars[i] < 'A' || chars[i] > 'F')) {
                return false;
            }
        }
        return true;
    }
    sz--; // don't want to loop to the last char, check it afterwords
          // for type qualifiers
    int i = start;
    // loop to the next to last char or to the last char if we need another digit to
    // make a valid number (e.g. chars[0..5] = "1234E")
    while (i < sz || (i < sz + 1 && allowSigns && !foundDigit)) {
        if (chars[i] >= '0' && chars[i] <= '9') {
            foundDigit = true;
            allowSigns = false;

        } else if (chars[i] == '.') {
            if (hasDecPoint || hasExp) {
                // two decimal points or dec in exponent   
                return false;
            }
            hasDecPoint = true;
        } else if (chars[i] == 'e' || chars[i] == 'E') {
            // we've already taken care of hex.
            if (hasExp) {
                // two E's
                return false;
            }
            if (!foundDigit) {
                return false;
            }
            hasExp = true;
            allowSigns = true;
        } else if (chars[i] == '+' || chars[i] == '-') {
            if (!allowSigns) {
                return false;
            }
            allowSigns = false;
            foundDigit = false; // we need a digit after the E
        } else {
            return false;
        }
        i++;
    }
    if (i < chars.length) {
        if (chars[i] >= '0' && chars[i] <= '9') {
            // no type qualifier, OK
            return true;
        }
        if (chars[i] == 'e' || chars[i] == 'E') {
            // can't have an E at the last byte
            return false;
        }
        if (chars[i] == '.') {
            if (hasDecPoint || hasExp) {
                // two decimal points or dec in exponent
                return false;
            }
            // single trailing decimal point after non-exponent is ok
            return foundDigit;
        }
        if (!allowSigns
            && (chars[i] == 'd'
                || chars[i] == 'D'
                || chars[i] == 'f'
                || chars[i] == 'F')) {
            return foundDigit;
        }
        if (chars[i] == 'l'
            || chars[i] == 'L') {
            // not allowing L with an exponent or decimal point
            return foundDigit && !hasExp && !hasDecPoint;
        }
        // last character is illegal
        return false;
    }
    // allowSigns is true iff the val ends in 'E'
    // found digit it to make sure weird stuff like '.' and '1E-' doesn't pass
    return !allowSigns && foundDigit;
}

[код находится под версией 2 лицензии Apache]

person A T    schedule 03.11.2013

Чтобы определить, является ли строка целочисленной или плавающей, и представить ее в более длинном формате.

Целое число

 String  cost=Long.MAX_VALUE+"";
  if (isNumeric (cost))    // returns false for non numeric
  {  
      BigInteger bi  = new BigInteger(cost);

  }

public static boolean isNumeric(String str) 
{ 
  NumberFormat formatter = NumberFormat.getInstance(); 
  ParsePosition pos = new ParsePosition(0); 
  formatter.parse(str, pos); 
  return str.length() == pos.getIndex(); 
} 
person Dead Programmer    schedule 31.03.2011
comment
Повторюсь: регулярное выражение допускает такие строки, как 99999999999999999999999, но синтаксический анализатор выдает исключение. То же самое касается регулярного выражения с плавающей запятой. - person Voo; 31.03.2011
comment
@Voo: для приведенного выше кода 99999999999999999999999 отлично работает. дайте мне знать, если я ошибаюсь? - person Dead Programmer; 31.03.2011
comment
Ваш код разрешает 99999999999999999999999 в качестве входных данных, что, если вы попытаетесь проанализировать его в if, вызовет исключение. Настоящая проблема заключается не только в том, является ли эта строка числом? но является ли эта строка числом и может быть представлена ​​как целое число? - person Voo; 31.03.2011

Вы можете избежать неприятного вида try/catch или регулярного выражения, используя класс Scanner:

String input = "123";
Scanner sc = new Scanner(input);
if (sc.hasNextInt())
    System.out.println("an int: " + sc.nextInt());
else {
    //handle the bad input
}
person nairbv    schedule 26.06.2012
comment
Сканер использует Integer.parseInt внутренне, поэтому не избежать попытки/поймать, а сметать их под ковер. - person Danubian Sailor; 14.05.2013
comment
Интересно... Не догадался посмотреть на источник. В Scanner.hasNext они сопоставляют строковый буфер с целочисленным шаблоном, поэтому я не уверен, что есть способ заставить его действительно выдавать nfe, даже внутри. Если вы проверили hasNextInt(), nextInt() тоже должен быть безопасным. - person nairbv; 14.05.2013
comment
@DanubianSailor: по крайней мере, вы избегаете try/catch в своем коде. Лучше иметь дело с логическим значением. - person Anna; 28.11.2017

Попробуйте преобразовать Приз в десятичный формат...

import java.math.BigDecimal;
import java.math.RoundingMode;

public class Bigdecimal {
    public static boolean isEmpty (String st) {
        return st == null || st.length() < 1; 
    }
    public static BigDecimal bigDecimalFormat(String Preis){        
        //MathContext   mi = new MathContext(2);
        BigDecimal bd = new BigDecimal(0.00);

                         bd = new BigDecimal(Preis);


            return bd.setScale(2, RoundingMode.HALF_UP);

        }
    public static void main(String[] args) {
        String cost = "12.12";
        if (!isEmpty(cost) ){
            try {
               BigDecimal intCost = bigDecimalFormat(cost);
               System.out.println(intCost);
               List<Book> books = bookService.findBooksCheaperThan(intCost);  
            } catch (NumberFormatException e) {
               System.out.println("This is not a number");
               System.out.println(e.getMessage());
            }
        }

}
}
person Narayan Yerrabachu    schedule 14.05.2013

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

cost.matches("-?\\d+\\.?\\d+")

для поплавка

а также

cost.matches("-?\\d+")

для целого числа

ИЗМЕНИТЬ

пожалуйста, обратите внимание на комментарий @Voo о max int

person Yaneeve    schedule 31.03.2011
comment
Это не решение! Регулярное выражение будет стоить = 99999999999999999999999 просто отлично, прежде чем синтаксический анализатор выдаст исключение. - person Voo; 31.03.2011
comment
Спасибо. Я думаю, что единственный способ получить 100% правильное регулярное выражение — это исчерпывающий подход для больших значений, что будет крайне неэффективно. Поэтому я боюсь, что в этом случае нам придется жить с плохим стилем неправильного использования исключений, пока Java не получит параметры. - person Voo; 31.03.2011

К сожалению, в Java нет способа избежать использования функции parseInt и просто перехватить исключение. Ну, теоретически вы могли бы написать свой собственный синтаксический анализатор, который проверяет, является ли это числом, но тогда вам вообще не нужен parseInt.

Метод регулярного выражения проблематичен, потому что ничто не мешает кому-то включить число > INTEGER.MAX_VALUE, которое пройдет тест регулярного выражения, но все равно не сработает.

person Voo    schedule 31.03.2011

Это зависит от вашего окружения. JSF, например, возьмет на себя бремя ручной проверки и преобразования строк ‹-> чисел от вас, еще одним вариантом является проверка бинов.

Что вы можете сделать немедленно в предоставленном фрагменте:

  1. Извлеките метод getAsInt(String param), в нем:
  2. Используйте String.isEmpty() (так как Java 6< /а>),
  3. окружить try / catch

О чем вам обязательно следует подумать, если вам случится написать много кода, подобного этому:

public void myBusinessMethod(@ValidNumber String numberInput) {
// ...    
}

(Это будет основано на перехватчике).

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

person jan groth    schedule 31.03.2011