How to avoid Number Format Exception in java? [duplicate]
Asked Answered
B

13

15

In my day to day web application development there are many instances where we need to take some number inputs from the user.

Then pass on this number input to may be service or DAO layer of the application.

At some stage since its a number (integer or float), we need to convert it into Integer as shown in the following code snippet.

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

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

Here in the above case I have to check if the input is not null or if there is no input (blank) or sometimes there is a possibility of a non number inputs e.g. blah, test etc.

What is the best possible way of handling such situations?

Bashemath answered 31/3, 2011 at 12:3 Comment(3)
if (cost !=null && !"".equals(cost) ) === if (!"".equals(cost)) ;)Fabulous
@peter-lawrey: Incorrect. What if cost == null? They're not the same check at all. The former expression would return false, the latter would return true.Henrique
What is a NumberFormatExceptionCw
A
27

Just catch your exception and do proper exception handling:

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());
        }
    }
Atop answered 31/3, 2011 at 12:8 Comment(6)
+1: I would add e.getMessage() to the error message.Fabulous
Yes. The sysout is only an example. he has to do proper exception handling anyway. But I'll add it. thanks.Atop
+1: Finally a sane answer. The only thing I would change is to move a call to bookService outside the try block, as this particular exception only applies to parsing cost string.Hoofer
@Alexander Pogrebnyak yes that's true but I think it depends on how the OP wants its logic. He can't find a cheaper book without a cost. (btw... you have the same name as a professional soccer player in the bundesliga :D)Atop
Should we really do this instead of a check like StringUtils.isNumeric(String) before calling parseInt(String)?Oath
I'm just curious as NumberFormatException is an unchecked exception if we should try to avoid the exception being thrown and prevent it as Learner says.Infelicitous
W
4

As always, the Jakarta Commons have at least part of the answer :

NumberUtils.isNumber()

This can be used to check most whether a given String is a number. You still have to choose what to do in case your String isnt a number ...

Witty answered 31/3, 2011 at 12:24 Comment(1)
Since this question is the top one when googling for NumberFormatException and your link seems to be dead, here is an alternative. NumberUtils.isNumber(). It currently still works, but is deprecated, so enjoy with a bit of caution.Mathura
P
2

Exceptions in recent versions of Java aren't expensive enough to make their avoidance important. Use the try/catch block people have suggested; if you catch the exception early in the process (i.e., right after the user has entered it) then you're not going to have the problem later in the process (because it'll be the right type anyway).

Exceptions used to be a lot more expensive than they are now; don't optimize for performance until you know the exceptions are actually causing a problem (and they won't, here.)

Parnell answered 31/3, 2011 at 12:18 Comment(0)
A
1

one posibility: catch the exception and show an error message within the user frontend.

edit: add an listener to the field within the gui and check the user inputs there too, with this solution the exception case should be very rare...

Antrum answered 31/3, 2011 at 12:7 Comment(5)
Exceptions should be used in exceptional circumstances, not for normal run-of-the-mill expected situations such as "nothing was input". Exceptions are expensive to set up.Henrique
True, but as long as we don't get out parameters in Java, you hardly get around (the regex method is faulty!). Also why would the exception be expensive to "set up"? It's only an entry in the exception table, so as long as you don't take the exceptional code path (and that should be rare) there's no real performance problem.Thayne
Think Voo is right, how else would you check this?Antrum
Sorry, what do you mean "you hardly get around"? And "get out parameters"?Henrique
"hardly get around [using the exception]" it should be. The correct regex approach would need an exhaustive list of numbers (all numbers < 10 digits ok, and then 2000000000|2000000001|..|2147483647). Out parameters would be obviously the best solution i.e. get a signature boolean tryParseInt(int val, out int erg) - see C# for one language that has this.Thayne
B
1

I suggest to do 2 things:

  • validate the input on client side before passing it to the Servlet
  • catch the exception and show an error message within the user frontend as Tobiask mentioned. This case should normally not happen, but never trust your clients. ;-)
Buttonhook answered 31/3, 2011 at 12:13 Comment(2)
IMHO Client side validation can only be a part of the validation process, but nothing you should rely on (unless you want to open the window for all kinds of attack patterns)...Invulnerable
That's what I said: never trust your clients. Still, it's something that should be checked on client-side as well. Simple input validation on server-side is not very efficient. As others mentioned there are probably frameworks/ libraries which can help you with this. Maybe even the new Bean Validation (Java EE 6)? (I haven't tested it yet.)Buttonhook
M
1

Documentation for the method from the Apache Commons Lang (from here):

Checks whether the String a valid Java number.

Valid numbers include hexadecimal marked with the 0x qualifier, scientific notation and numbers marked with a type qualifier (e.g. 123L).

Null and empty String will return false.

Parameters:

`str` - the `String` to check

Returns:

`true` if the string is a correctly formatted number

isNumber from 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;
}

[code is under version 2 of the Apache License]

Mandell answered 3/11, 2013 at 1:59 Comment(0)
C
1
public class Main {
    public static void main(String[] args) {

        String number;

        while(true){

            try{
                number = JOptionPane.showInputDialog(null);

                if( Main.isNumber(number) )
                    break;

            }catch(NumberFormatException e){
                System.out.println(e.getMessage());
            }

        }

        System.out.println("Your number is " + number);

    }

    public static boolean isNumber(Object o){
        boolean isNumber = true;

        for( byte b : o.toString().getBytes() ){
            char c = (char)b;
            if(!Character.isDigit(c))
                isNumber = false;
        }

        return isNumber;
    }

}
Crockery answered 5/1, 2014 at 9:27 Comment(0)
H
0

To Determine if a string is Int or Float and to represent in longer format.

Integer

 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(); 
} 
Heliogravure answered 31/3, 2011 at 12:9 Comment(3)
To repeat myself: The regex will allow strings like "9999999999999999999999", but the parser will throw an exception. The same goes for the float regex.Thayne
@Voo: for the above code 9999999999999999999999 works fine. let me know if am wrong?Heliogravure
Your code does allow 9999999999999999999999 as input, which if you try to parse it in the if will throw an exception. The real problem isn't just "is this string a number?" but "Is this string a number and can be represented as an integer?".Thayne
B
0

You can avoid the unpleasant looking try/catch or regex by using the Scanner class:

String input = "123";
Scanner sc = new Scanner(input);
if (sc.hasNextInt())
    System.out.println("an int: " + sc.nextInt());
else {
    //handle the bad input
}
Bathe answered 26/6, 2012 at 8:16 Comment(3)
Scanner is using Integer.parseInt internally so there is no avoiding try/catch but sweeping them under the carpet.Zambia
Interesting... I didn't think to look at the source. In Scanner.hasNext they match the string buffer against an integer pattern though, so I'm not convinced there's a way to make it actually throw an nfe, even internally. If you've checked hasNextInt(), nextInt() should be safe too.Bathe
@DanubianSailor: at least you avoid the try/catch in your code. It's nicer to deal with a boolean.Insessorial
E
0

Try to convert Prize into decimal format...

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());
            }
        }

}
}
Exterritorial answered 14/5, 2013 at 6:28 Comment(0)
C
-1

I don't know about the runtime disadvantages about the following but you could run a regexp match on your string to make sure it is a number before trying to parse it, thus

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

for a float

and

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

for an integer

EDIT

please notices @Voo's comment about max int

Caudill answered 31/3, 2011 at 12:8 Comment(2)
That's no solution! The regex will cost = "9999999999999999999999" just fine, before the parser throws the exception.Thayne
Thanks. I think the only way to get a 100% correct regex would be an exhaustive approach for the larger values, which will be extremely inefficient. So I fear we'll have to live with the bad style of "misusing" exceptions in this case until Java gets out parameters.Thayne
T
-1

In Java there's sadly no way you can avoid using the parseInt function and just catching the exception. Well you could theoretically write your own parser that checks if it's a number, but then you don't need parseInt at all anymore.

The regex method is problematic because nothing stops somebody from including a number > INTEGER.MAX_VALUE which will pass the regex test but still fail.

Thayne answered 31/3, 2011 at 12:9 Comment(0)
I
-1

That depends on your environment. JSF for example would take the burden of manually checking and converting Strings <-> Numbers from you, Bean Validation is another option.

What you can do immediately in the snippet you provide:

  1. Extract method getAsInt(String param), in it:
  2. Use String.isEmpty() (Since Java 6),
  3. surround with try / catch

What you should definitely think about if you happen to write a lot of code like this:

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

(This would be interceptor-based).

Last but not least: Save the work and try to switch to a framework which gives you support for these common tasks...

Invulnerable answered 31/3, 2011 at 12:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.