【问题标题】:Most elegant way to detect if a String is a number?检测字符串是否为数字的最优雅方法?
【发布时间】:2010-09-26 10:54:55
【问题描述】:

有没有比

更好、更优雅(和/或可能更快)的方法
boolean isNumber = false;
try{
   Double.valueOf(myNumber);
   isNumber = true;
} catch (NumberFormatException e) {
}

...?


编辑: 由于我无法选择两个答案,我将使用正则表达式一个,因为 a) 它很优雅 b) 说“Jon Skeet 解决了问题”是重言式,因为 Jon Skeet 自己是所有问题的解决方案。

【问题讨论】:

  • 当用户输入需要是数字时,我就是这样做的。然而,从来没有遇到过其他任何事情。
  • 不久前我问了一个非常相似的问题。其中一些答案可能会有所帮助。 stackoverflow.com/questions/237159/…
  • Jajajajaja 很好的回答 Epaga。

标签: java string parsing numbers


【解决方案1】:

我会一如既往地使用Jakarta commons-lang!但我不知道their implementation 是否快。它不依赖异常,这可能是一个很好的性能明智...

【讨论】:

  • commons-lang 还可以检测 Java 无法检测到的内容,例如:多个逗号、数字周围的空格等等。
  • 这取决于你想做什么。有些数字不能用 Java 双精度表示 - 是否应该包括在内?如果您真的想知道最终是否能够转换为 Java double,我怀疑如果不调用 Double,您将很难可靠地做到这一点。
【解决方案2】:

我更喜欢在字符串的 char[] 表示上使用循环并使用 Character.isDigit() 方法。如果需要优雅,我认为这是最可读性

package tias;

public class Main {
  private static final String NUMERIC = "123456789";
  private static final String NOT_NUMERIC = "1L5C";

  public static void main(String[] args) {
    System.out.println(isStringNumeric(NUMERIC));
    System.out.println(isStringNumeric(NOT_NUMERIC));
  }

  private static boolean isStringNumeric(String aString) {
    if (aString == null || aString.length() == 0) {
      return false;
    }
    for (char c : aString.toCharArray() ) {
      if (!Character.isDigit(c)) {
        return false;
      }
    }
    return true;
  }

}

【讨论】:

    【解决方案3】:

    您可以使用正则表达式,例如 String.matches("^[\\d\\-\\.]+$");(如果您不测试负数或浮点数,您可以稍微简化一下)。

    不确定这是否会比您概述的方法更快。

    编辑:鉴于所有这些争议,我决定进行测试并获取一些关于这些方法的速度有多快的数据。与其说是正确性,不如说是他们跑得有多快。

    您可以阅读我的结果on my blog。 (提示:Jon Skeet FTW)。

    【讨论】:

    • 负数和非整数也会失败:)
    • 有没有人测试过一个正则表达式是否比下面 Jon Skeet 的方法更快或更慢?如果您能找到一个适用于您期望的数字的正则表达式,那么这个正则表达式当然看起来更简洁。
    • 有些人在遇到问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。 ;)
    • @Paul:我很久以前就使用 .NET 进行过测试,而正则表达式的速度明显变慢。如果您想使用正则表达式,至少构建一次并编译它:)(我怀疑它仍然会更慢。顺便说一句,我的代码现在涵盖的情况比原来多一些,而且它开始变得复杂了。 ..)
    • 这个答案和 john skeet 都假定小数点分隔符是 .而且您也不允许使用千位分隔符。
    【解决方案4】:

    如果您想要速度极快的东西,并且非常清楚要接受哪些格式,则可以手动构建状态机DFA。无论如何,这基本上是正则表达式在后台工作的方式,但您可以通过这种方式避免正则表达式编译步骤,而且它可能比通用正则表达式编译器更快。

    【讨论】:

    • 虽然这在理论上是一个有趣的答案,但在这里并没有真正有用,因为您没有提供有关如何构建这种机器并在上下文中使用它的信息。
    【解决方案5】:

    Double javadocs 中实际上给出了正确的正则表达式:

    为避免在无效字符串上调用此方法并引发 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
        }
    

    但是,这不允许本地化表示:

    要解释浮点值的本地化字符串表示,请使用NumberFormat 的子类。

    【讨论】:

      【解决方案6】:

      我不相信 Java 中内置了任何东西可以更快且更可靠地执行此操作,假设稍后您会想要使用 Double.valueOf (或类似的)实际解析它。

      我会使用 Double.parseDouble 而不是 Double.valueOf 来避免不必要地创建 Double,并且您还可以通过检查数字来比异常更快地摆脱明显愚蠢的数字,e /E, - 和 .预先。所以,类似:

      public boolean isDouble(String value)
      {        
          boolean seenDot = false;
          boolean seenExp = false;
          boolean justSeenExp = false;
          boolean seenDigit = false;
          for (int i=0; i < value.length(); i++)
          {
              char c = value.charAt(i);
              if (c >= '0' && c <= '9')
              {
                  seenDigit = true;
                  continue;
              }
              if ((c == '-' || c=='+') && (i == 0 || justSeenExp))
              {
                  continue;
              }
              if (c == '.' && !seenDot)
              {
                  seenDot = true;
                  continue;
              }
              justSeenExp = false;
              if ((c == 'e' || c == 'E') && !seenExp)
              {
                  seenExp = true;
                  justSeenExp = true;
                  continue;
              }
              return false;
          }
          if (!seenDigit)
          {
              return false;
          }
          try
          {
              Double.parseDouble(value);
              return true;
          }
          catch (NumberFormatException e)
          {
              return false;
          }
      }
      

      请注意,尽管尝试了几次,仍然不包括“NaN”或十六进制值。您是否希望这些通过取决于上下文。

      根据我的经验,正则表达式比上面的硬编码检查要慢。

      【讨论】:

      • 根据我的经验,我通常希望 Double and 知道它是否是合法的 double,所以我可能会更改您的方法以返回 Double 或抛出 NumberFormatException .
      • ..或者返回一个带有布尔值和双精度值的小类,或者使用越界双精度值,具体取决于需要。
      • @Paul:确实。那时,您基本上已经实现了 .NET 的“bool Double.TryParse(string text, out double)。在 Java 中,另一种方法是返回 Double(对象)或 null 来表示“无效”。
      • 嘘... +1.0e-7 是有效的双精度数。
      • 嘘......它仍然会失败。 double 中有两个可能的符号:在尾数之前和 e/E 之后,符号可以是“+”。这里实际上有一个教训,那就是双精度的规范比您的代码更复杂:如果 ("0123456789eE-+".Contains(c)) continue; 则最好这样做;
      【解决方案7】:

      利用斯基特先生:

      private boolean IsValidDoubleChar(char c)
      {
          return "0123456789.+-eE".indexOf(c) >= 0;
      }
      
      public boolean isDouble(String value)
      {
          for (int i=0; i < value.length(); i++)
          {
              char c = value.charAt(i);
              if (IsValidDoubleChar(c))
                  continue;
              return false;
          }
          try
          {
              Double.parseDouble(value);
              return true;
          }
          catch (NumberFormatException e)
          {
              return false;
          }
      }
      

      【讨论】:

      • 1 尝试这个让我在它应该是真的时候是假的:P
      【解决方案8】:

      参见java.text.NumberFormat (javadoc)。

      NumberFormat nf = NumberFormat.getInstance(Locale.FRENCH);
      Number myNumber = nf.parse(myString);
      int myInt = myNumber.intValue();
      double myDouble = myNumber.doubleValue();
      

      【讨论】:

      • 很高兴看到一个不做语言环境假设的答案。 +1
      • "全球社区" - 你必须有正确的语言环境格式化/解析。德国的 1.024,00 是巴黎的 1 024.00 和纽约的 1,024.00。
      【解决方案9】:

      这些答案中的大多数是在某种程度上可以接受的解决方案。对于您可能关心的所有情况,所有正则表达式解决方案都存在不正确的问题。

      如果您真的想确保字符串是有效数字,那么我会使用您自己的解决方案。不要忘记,我想,大多数时候 String 将是一个 valid 数字并且不会引发异常。所以大多数时候性能会和 Double.valueOf() 一样。

      我想这真的不是一个答案,只是它验证了你最初的直觉。

      兰迪

      【讨论】:

      • 我喜欢正则表达式的方法。虽然确实提供的内容可能不符合 OP 的要求,但这仅仅是因为要求含糊不清。提出开发解决方案的方法是我们能做的最好的事情。
      • 我明白你在说什么。如果目标只是检测数字,那么 [0-9]+ 就可以了。
      【解决方案10】:

      在 Apache Commons 中使用 StringUtils.isDouble(String)

      【讨论】:

      • 移入较新版本。请参阅 org.apache.commons.lang3.math.NumberUtils。特别是 NumberUtils.isParseable() 和 NumberUtils.isCreatable()。
      【解决方案11】:

      按照 Phill 的回答,我可以建议另一个正则表达式吗?

      String.matches("^-?\\d+(\\.\\d+)?$");
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-09-11
        • 1970-01-01
        • 2022-11-23
        • 1970-01-01
        • 1970-01-01
        • 2012-12-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多