【问题标题】:How to avoid Number Format Exception in java? [duplicate]如何避免java中的数字格式异常? [复制]
【发布时间】:2011-07-26 20:26:15
【问题描述】:

在我日常的 Web 应用程序开发中,有很多情况下我们需要从用户那里获取一些数字输入。

然后将此数字输入传递给可能是应用程序的服务或DAO层。

在某个阶段,因为它是一个数字(整数或浮点数),我们需要将其转换为整数,如以下代码 sn-p 所示。

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

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

在上述情况下,我必须检查输入是否不为空,或者是否没有输入(空白),或者有时可能存在非数字输入,例如废话,测试等。

处理这种情况的最佳方法是什么?

【问题讨论】:

  • if (cost !=null &amp;&amp; !"".equals(cost) ) === if (!"".equals(cost)) ;)
  • @peter-lawrey:不正确。如果成本 == 空怎么办?它们根本不是同一张支票。前一个表达式将返回 false,后者将返回 true。

标签: java numberformatexception


【解决方案1】:

一种可能性:捕获异常并在用户前端显示错误消息。

编辑:在 gui 中的字段中添加一个侦听器并检查那里的用户输入,使用此解决方案,异常情况应该非常罕见......

【讨论】:

  • Exceptions 应该在特殊情况下使用,not 用于正常的普通预期情况,例如“没有输入任何内容”。设置异常的成本很高。
  • 是的,但只要我们在 Java 中不取出参数,你就很难绕过(正则表达式方法有问题!)。另外,为什么“设置”异常会很昂贵?它只是异常表中的一个条目,因此只要您不采用异常代码路径(这应该很少见),就不会出现真正的性能问题。
  • 认为 Voo 是对的,否则你会如何检查?
  • 抱歉,“你几乎没有走动”是什么意思?以及“取出参数”?
  • “很难绕过[使用异常]”它应该是。正确的正则表达式方法需要一个详尽的数字列表(所有数字
【解决方案2】:

我不知道以下运行时的缺点,但您可以在字符串上运行正则表达式匹配以确保它是一个数字,然后再尝试解析它,因此

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

浮动

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

对于整数

编辑

请注意@Voo 关于 max int 的评论

【讨论】:

  • 没办法!在解析器抛出异常之前,正则表达式将花费 = "9999999999999999999999" 就好了。
  • 谢谢。我认为获得 100% 正确正则表达式的唯一方法是对较大的值采用详尽的方法,这将是非常低效的。所以我担心在这种情况下我们将不得不忍受“滥用”异常的糟糕风格,直到 Java 得到参数。
【解决方案3】:

只需捕获您的异常并进行适当的异常处理:

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

【讨论】:

  • +1:我会将 e.getMessage() 添加到错误消息中。
  • 是的。 sysout 只是一个示例。无论如何,他必须进行适当的异常处理。但我会添加它。谢谢。
  • +1:最后一个理智的答案。我唯一要改变的是将调用移动到 try 块之外的 bookService,因为这个特殊的异常仅适用于解析 cost 字符串。
  • @Alexander Pogrebnyak 是的,这是真的,但我认为这取决于 OP 如何希望其逻辑。他找不到不花钱的便宜书。 (顺便说一句……你和德甲职业足球运动员同名:D)
  • 在调用parseInt(String)之前,我们真的应该这样做而不是像StringUtils.isNumeric(String)这样的检查吗?
【解决方案4】:

遗憾的是,在 Java 中,您无法避免使用 parseInt 函数并仅捕获异常。那么理论上你可以编写自己的解析器来检查它是否是一个数字,但是你根本不需要 parseInt 了。

正则表达式方法是有问题的,因为没有什么能阻止某人包含一个数字 > INTEGER.MAX_VALUE 这将通过正则表达式测试但仍然失败。

【讨论】:

    【解决方案5】:

    判断一个字符串是 Int 还是 Float 并以更长的格式表示。

    整数

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

    【讨论】:

    • 再说一遍:正则表达式将允许像“9999999999999999999999”这样的字符串,但解析器会抛出异常。浮动正则表达式也是如此。
    • @Voo: 对于上述代码 9999999999999999999999 工作正常。如果有错请告诉我?
    • 您的代码确实允许 9999999999999999999999 作为输入,如果您尝试在 if 中对其进行解析,则会引发异常。真正的问题不仅仅是“这个字符串是数字吗?”但是“这个字符串是一个数字并且可以表示为一个整数吗?”。
    【解决方案6】:

    这取决于您的环境。例如,JSF 将承担手动检查和转换字符串 数字的负担,Bean Validation 是另一种选择。

    您可以在您提供的 sn-p 中立即做什么:

    1. 提取方法getAsInt(String param),在里面:
    2. 使用String.isEmpty()(自Java 6),
    3. try / catch包围

    如果你碰巧写了很多这样的代码,你肯定应该考虑

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

    (这将是基于拦截器的)。

    最后但并非最不重要的一点:保存工作并尝试切换到为这些常见任务提供支持的框架...

    【讨论】:

      【解决方案7】:

      我建议做两件事:

      • 在将输入传递给 Servlet 之前在客户端验证输入
      • 捕获异常并在用户前端显示错误消息,如 Tobiask 所述。这种情况通常不应该发生,但永远不要相信你的客户。 ;-)

      【讨论】:

      • 恕我直言,客户端验证只能是验证过程的一部分,但您不应该依赖任何东西(除非您想为各种攻击模式打开窗口)...
      • 这就是我所说的:永远不要相信你的客户。尽管如此,它也应该在客户端进行检查。服务器端的简单输入验证效率不高。正如其他人提到的那样,可能有框架/库可以帮助您解决这个问题。甚至可能是新的 Bean Validation (Java EE 6)? (我还没有测试过。)
      【解决方案8】:

      最近版本的 Java 中的异常并不足以使避免它们变得重要。使用人们建议的 try/catch 块;如果您在流程的早期捕获异常(即,在用户输入之后),那么您将不会在流程的后期遇到问题(因为无论如何它都是正确的类型)。

      过去的例外情况比现在要昂贵得多;在您知道异常实际上会导致问题之前,不要优化性能(在这里它们不会。)

      【讨论】:

        【解决方案9】:

        与往常一样,雅加达公地至少有部分答案:

        NumberUtils.isNumber()

        这可用于检查给定字符串是否为数字。如果您的字符串不是数字,您仍然必须选择要做什么...

        【讨论】:

        • 由于在谷歌上搜索NumberFormatException 时这个问题是最重要的问题,并且您的链接似乎已失效,因此这里有一个替代方案。 NumberUtils.isNumber()。它目前仍然有效,但已被弃用,因此请谨慎使用。
        【解决方案10】:

        您可以通过使用 Scanner 类来避免看起来不愉快的 try/catch 或正则表达式:

        String input = "123";
        Scanner sc = new Scanner(input);
        if (sc.hasNextInt())
            System.out.println("an int: " + sc.nextInt());
        else {
            //handle the bad input
        }
        

        【讨论】:

        • 扫描仪在内部使用 Integer.parseInt,因此无法避免 try/catch 而是将它们扫到地毯下。
        • 有趣...我没想到要看源码。不过,在 Scanner.hasNext 中,它们将字符串缓冲区与整数模式匹配,所以我不相信有办法让它实际上抛出 nfe,即使在内部也是如此。如果你检查了 hasNextInt(),nextInt() 也应该是安全的。
        • @DanubianSailor:至少你避免了代码中的 try/catch。处理布尔值会更好。
        【解决方案11】:

        尝试将奖品转换为十进制格式...

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

        【讨论】:

          【解决方案12】:

          来自 Apache Commons Lang (from here) 的方法文档:

          检查字符串是否为有效的 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;
          }
          

          [代码在 Apache 许可证的第 2 版下]

          【讨论】:

            【解决方案13】:
            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;
                }
            
            }
            

            【讨论】:

              猜你喜欢
              • 2019-06-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-03-05
              • 1970-01-01
              • 2023-03-19
              相关资源
              最近更新 更多