【问题标题】:Java library to check whether a String contains a number *without* exceptions用于检查字符串是否包含*无*异常的数字的 Java 库
【发布时间】:2010-11-12 21:54:04
【问题描述】:

如果传递的字符串是有效数字(例如“123.55e-9”、“-333,556”),我正在寻找一种返回布尔值的方法。我不想只想做:

public boolean isANumber(String s) {
    try { 
        BigDecimal a = new BigDecimal(s); 
        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}

显然,该函数应该使用状态机 (DFA) 来解析字符串,以确保无效示例不会欺骗它(例如“-21,22.22.2”、“33-2”)。你知道是否存在这样的图书馆吗?我真的不想自己写,因为这是一个显而易见的问题,我敢肯定我会重新发明轮子。

谢谢,

尼克

【问题讨论】:

  • 为什么你不想使用 BigDecimal 的解析?这是最简单的方法,真的。
  • 我想知道你为什么不想使用你给的sn-p?如果你需要使用你必须解析的数字?只是为了验证一个字符串可以按原样进入后端吗?
  • 因为这种情况没有例外。例外是,好吧,不是程序控制的例外。
  • 我也遇到过几次这个问题,虽然以这种方式利用异常感觉不正确,但它仍然是最简单的方法。它很简单,经过良好测试并且有效。我不再寻找其他方式。
  • 理论上我同意这一点。如果您可以控制 BigDecimal 中使用的解析,则可以将其更改为返回一些指示解析失败的值,而不是抛出异常。但是,由于您无法控制它,我不会为已经存在的东西制作自己的实现,只是因为 BigDecimal 方式“不是很好,因为它使用异常”。

标签: java format numbers bigdecimal state-machine


【解决方案1】:

Javadocs 中为Double.valueOf(String) 指定了确切的正则表达式。

为避免在无效字符串上调用此方法并抛出NumberFormatException,可以使用下面的正则表达式来筛选输入字符串:

final String Digits     = "(\\p{Digit}+)";
final String HexDigits  = "(\\p{XDigit}+)";
// an exponent is 'e' or 'E' followed by an optionally 
// signed decimal integer.
final String Exp        = "[eE][+-]?"+Digits;
final String fpRegex    =
       ("[\\x00-\\x20]*"+  // Optional leading "whitespace"
        "[+-]?(" + // Optional sign character
        "NaN|" +           // "NaN" string
        "Infinity|" +      // "Infinity" string

        // A decimal floating-point string representing a finite positive
        // number without a leading sign has at most five basic pieces:
        // Digits . Digits ExponentPart FloatTypeSuffix
        // 
        // Since this method allows integer-only strings as input
        // in addition to strings of floating-point literals, the
        // two sub-patterns below are simplifications of the grammar
        // productions from the Java Language Specification, 2nd 
        // edition, section 3.10.2.

        // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
        "((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+

        // . Digits ExponentPart_opt FloatTypeSuffix_opt
        "(\\.("+Digits+")("+Exp+")?)|"+

        // Hexadecimal strings
        "((" +
        // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
        "(0[xX]" + HexDigits + "(\\.)?)|" +

        // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
        "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +

        ")[pP][+-]?" + Digits + "))" +
        "[fFdD]?))" +
        "[\\x00-\\x20]*"); // Optional trailing "whitespace"
       
if (Pattern.matches(fpRegex, myString))
    Double.valueOf(myString); // Will not throw NumberFormatException
else {
    // Perform suitable alternative action
}

【讨论】:

    【解决方案2】:

    我会避免重新发明这种方法并使用 Apache Commons。如果您使用 Spring、Struts 或许多其他常用的 java 库,它们通常包含 Apache commons。您将需要 commons-lang.jar 文件。这是您想要的NumberUtils 中的方法:

    isNumber[1]
    
    public static boolean isNumber(java.lang.String str)
    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
    

    【讨论】:

      【解决方案3】:

      是的,正则表达式应该可以解决问题。我只知道 .Net 正则表达式,但所有正则表达式语言都非常相似,所以这应该可以帮助您入门。我没有对其进行测试,因此您可能想使用 Java regex 类来尝试一下。

      "-?(([0-9]{1,3}(,[0-9{3,3})*)|[0-9]*)(\.[0-9]+(e-?[0-9]*)?)?"
      

      一些正则表达式控制语法:
      ? - 可选元素
      | - OR 运算符。基本上,如果格式正确,我允许带或不带逗号的数字。
      [ ] - 允许的字符集
      { , } - 元素的最小最大值
      * - 任意数量的元素,从 0 到无穷大
      + - 至少一个元素,从 1 到无穷大
      \ - 转义字符
      . - 任何字符(因此它被转义)

      【讨论】:

      • 实际上这个表达式会匹配 22,22,2.14123415e1
      【解决方案4】:

      这是一个基于正则表达式的实用函数,工作正常(在保持其可读性的同时不适合正则表达式中的“”检查):

      public class TestRegexp {
          static final String NUM_REGEX=
              "-?((([0-9]{1,3})(,[0-9]{3})*)|[0-9]*)(\\.[0-9]+)?([Ee][0-9]*)?";
          public static boolean isNum(String s) {
                  return s!=null && s.length()>0 && s.matches(NUM_REGEX);  
          }
          public static void main(String[]args) {
              String[] values={
                      "",
                      "0",
                      "0.1",
                      ".1",
                      "-.5E5",
                      "-12,524.5E5",
                      "-452,456,456,466.5E5",
                      "-452,456,456,466E5",
                      "22,22,2.14123415e1",
              };
              for (String value : values) {
                  System.out.println(value+" is a number: "
                  +isNum(value));
              }
          }
      
      }
      

      【讨论】:

        【解决方案5】:

        使用regexp

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-29
        • 2013-11-20
        • 1970-01-01
        • 2014-12-27
        • 2020-11-28
        相关资源
        最近更新 更多