8

有没有更好,更优雅(和/或可能更快)的方式比

boolean isNumber = false;
try{
   Double.valueOf(myNumber);
   isNumber = true;
} catch (NumberFormatException e) {
}

...?


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

4

11 回答 11

10

我不相信 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”或十六进制值。您是否希望这些通过取决于上下文。

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

于 2008-12-11T15:00:20.040 回答
9

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

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

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

你可以在我的博客上阅读我的结果。(提示:Jon Skeet FTW)。

于 2008-12-11T14:59:56.297 回答
8

请参阅java.text.NumberFormat (javadoc)。

NumberFormat nf = NumberFormat.getInstance(Locale.FRENCH);
Number myNumber = nf.parse(myString);
int myInt = myNumber.intValue();
double myDouble = myNumber.doubleValue();
于 2008-12-11T16:01:52.197 回答
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的子类。

于 2008-12-12T16:23:56.617 回答
3

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

于 2008-12-11T15:04:02.187 回答
3

利用斯基特先生:

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;
    }
}
于 2008-12-11T16:05:15.963 回答
2

我会一如既往地使用Jakarta commons-lang !但我不知道他们的实施是否很快。它不依赖于异常,这可能是一个很好的表现......

于 2008-12-11T15:01:53.300 回答
2

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

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

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

兰迪

于 2008-12-11T15:21:42.730 回答
1

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

String.matches("^-?\\d+(\\.\\d+)?$");
于 2008-12-11T15:14:42.447 回答
1

我更喜欢在字符串的 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;
  }

}

于 2008-12-15T13:53:16.230 回答
-1

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

于 2008-12-12T16:24:19.283 回答