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?

ajm
  • 12,863
  • 58
  • 163
  • 234

13 Answers13

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());
        }
    }
RoflcoptrException
  • 51,941
  • 35
  • 152
  • 200
  • +1: I would add e.getMessage() to the error message. – Peter Lawrey Mar 31 '11 at 12:11
  • Yes. The sysout is only an example. he has to do proper exception handling anyway. But I'll add it. thanks. – RoflcoptrException Mar 31 '11 at 12:12
  • 2
    +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. – Alexander Pogrebnyak Mar 31 '11 at 12:30
  • @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) – RoflcoptrException Mar 31 '11 at 12:32
  • Should we really do this instead of a check like `StringUtils.isNumeric(String)` before calling `parseInt(String)`? – Learner Mar 21 '17 at 22:54
  • 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. – Andrew S Oct 04 '21 at 15:18
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 ...

Guillaume
  • 18,494
  • 8
  • 53
  • 74
  • Since this question is the top one when googling for `NumberFormatException` and your link seems to be dead, here is an alternative. [NumberUtils.isNumber()](https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/math/NumberUtils.html#isNumber-java.lang.String-). It currently still works, but is deprecated, so enjoy with a bit of caution. – cluuu Apr 21 '21 at 07:26
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.)

Joseph Ottinger
  • 4,911
  • 1
  • 22
  • 23
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]

Community
  • 1
  • 1
A T
  • 13,008
  • 21
  • 97
  • 158
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;
    }

}
vahid abdi
  • 9,636
  • 4
  • 29
  • 35
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...

Tobias
  • 7,238
  • 10
  • 46
  • 77
  • 1
    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. – occulus Mar 31 '11 at 12:08
  • 1
    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. – Voo Mar 31 '11 at 12:10
  • Think Voo is right, how else would you check this? – Tobias Mar 31 '11 at 12:12
  • Sorry, what do you mean "you hardly get around"? And "get out parameters"? – occulus Mar 31 '11 at 12:16
  • "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. – Voo Mar 31 '11 at 12:24
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. ;-)
Puce
  • 37,247
  • 13
  • 80
  • 152
  • 1
    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)... – Jan Groth Mar 31 '11 at 12:16
  • 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.) – Puce Mar 31 '11 at 12:25
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
}
cHao
  • 84,970
  • 20
  • 145
  • 172
nairbv
  • 4,045
  • 1
  • 24
  • 26
  • Scanner is using Integer.parseInt internally so there is no avoiding try/catch but sweeping them under the carpet. – Danubian Sailor May 14 '13 at 07:26
  • 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. – nairbv May 14 '13 at 19:19
  • @DanubianSailor: at least you avoid the try/catch in your code. It's nicer to deal with a boolean. – Anna Nov 28 '17 at 08:43
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());
            }
        }

}
}
Nunser
  • 4,512
  • 8
  • 25
  • 37
Narayan Yerrabachu
  • 1,714
  • 1
  • 19
  • 31
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(); 
} 
Dead Programmer
  • 12,427
  • 23
  • 80
  • 112
  • To repeat myself: The regex will allow strings like "9999999999999999999999", but the parser will throw an exception. The same goes for the float regex. – Voo Mar 31 '11 at 12:14
  • @Voo: for the above code 9999999999999999999999 works fine. let me know if am wrong? – Dead Programmer Mar 31 '11 at 12:23
  • 1
    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?". – Voo Mar 31 '11 at 12:28
-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

Yaneeve
  • 4,751
  • 10
  • 49
  • 87
  • 1
    That's no solution! The regex will cost = "9999999999999999999999" just fine, before the parser throws the exception. – Voo Mar 31 '11 at 12:12
  • 1
    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. – Voo Mar 31 '11 at 12:20
-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.

Voo
  • 29,040
  • 11
  • 82
  • 156
-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...

Jan Groth
  • 14,039
  • 5
  • 40
  • 55