【问题标题】:How to find out if the value contained in a string is double or not如何找出字符串中包含的值是否为double
【发布时间】:2011-03-09 04:57:15
【问题描述】:

在 Java 中,我试图找出字符串中包含的值是否为双精度值?

【问题讨论】:

  • 你成为替身的标准是什么?例如,如果您的字符串是 100,它会是双精度的,但也是长的。
  • “double or not”,你的意思是double还是整数还是别的什么?
  • 你所说的“双倍”是什么意思?你的意思是,它是一个需要双精度的浮点数(与我们可以简单地称为“float”的单精度相反)?或者任何可以是双倍的数字? (所以,Double.parseDouble 不会失败,比如 5)
  • 您可以尝试将其与正则表达式匹配,如下所述:regular-expressions.info/floatingpoint.html 相关问题:stackoverflow.com/questions/1295327/…stackoverflow.com/questions/2293780/…
  • 您认为字符串“Infinity”是双精度数吗?仅供参考,Double.parseDouble 让它通过:-)

标签: java


【解决方案1】:
    boolean isDouble(String str) {
        try {
            Double.parseDouble(str);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

【讨论】:

  • 这并不完全有效。例如。 49d 或 49.d 在这里返回 true,在这种情况下通常不需要。
  • 我们可以这样写 if(Double.isNaN(Double.parseDouble(str))){ return false; } 其他 { 返回真; }
【解决方案2】:

source for Double 中有关于此的说明:

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

下面的正则表达式的最终形式很长:

[\x00-\x20]*[+-]?(NaN|Infinity|((((\p{Digit}+)(\.)?((\p{Digit}+)?)([eE][+-]?(\p{Digit}+))?)|(\.((\p{Digit}+))([eE][+-]?(\p{Digit}+))?)|(((0[xX](\p{XDigit}+)(\.)?)|(0[xX](\p{XDigit}+)?(\.)(\p{XDigit}+)))[pP][+-]?(\p{Digit}+)))[fFdD]?))[\x00-\x20]*

但是,使用此方法,您可以轻松排除一些特殊的双精度值,例如 InfinityNaN,它们都被 Double.parseDouble 接受。比如这样:

String regExp = "[\\x00-\\x20]*[+-]?(((((\\p{Digit}+)(\\.)?((\\p{Digit}+)?)([eE][+-]?(\\p{Digit}+))?)|(\\.((\\p{Digit}+))([eE][+-]?(\\p{Digit}+))?)|(((0[xX](\\p{XDigit}+)(\\.)?)|(0[xX](\\p{XDigit}+)?(\\.)(\\p{XDigit}+)))[pP][+-]?(\\p{Digit}+)))[fFdD]?))[\\x00-\\x20]*";
boolean matches = yourString.matches(regExp);

【讨论】:

  • 我将此添加到基准测试中,它比Double.parseDouble(s) 解决方案慢一点。我想在解析之前检查保护块中的特殊双精度值将是解决此问题的最佳方法。
  • 是的,我同意。 parseDouble 似乎相当优化:docjar.com/html/api/sun/misc/FloatingDecimal.java.html (readJavaFormatString)
  • 现在我预编译了正则表达式,它的性能优于Double.parseDouble(s) 方法。
  • @aioobe +1 真正深入了解。
【解决方案3】:

使用Scanner 会比使用Double.parseDouble(String s) 慢很多。

private static Random rand = new Random();
private static final String regExp = "[\\x00-\\x20]*[+-]?(((((\\p{Digit}+)(\\.)?((\\p{Digit}+)?)([eE][+-]?(\\p{Digit}+))?)|(\\.((\\p{Digit}+))([eE][+-]?(\\p{Digit}+))?)|(((0[xX](\\p{XDigit}+)(\\.)?)|(0[xX](\\p{XDigit}+)?(\\.)(\\p{XDigit}+)))[pP][+-]?(\\p{Digit}+)))[fFdD]?))[\\x00-\\x20]*";
private static final Pattern pattern = Pattern.compile(regExp);

public static void main(String[] args) {

    int trials = 50000;
    String[] values = new String[trials];

    // initialize the array
    // about half the values will be parsable as double
    for( int i = 0; i < trials; ++i ) {
        double d = rand.nextDouble();
        boolean b = rand.nextBoolean();

        values[i] = (b ? "" : "abc") + d;
    }

    long start = System.currentTimeMillis();

    int parseCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleParse(values[i]) ) {
            parseCount++;
        }
    }

    long end = System.currentTimeMillis();
    long elapsed = end - start;

    System.out.println("Elapsed time parsing: " + elapsed + " ms");
    System.out.println("Doubles: " + parseCount);

    // reset the timer for the next run
    start = System.currentTimeMillis();

    int scanCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleScan(values[i]) ) {
            scanCount++;
        }
    }

    end = System.currentTimeMillis();
    elapsed = end - start;

    System.out.println("Elapsed time scanning: " + elapsed + " ms");
    System.out.println("Doubles: " + scanCount);


    // reset the timer for the next run
    start = System.currentTimeMillis();

    int regexCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleRegex(values[i]) ) {
            regexCount++;
        }
    }

    end = System.currentTimeMillis();
    elapsed = end - start;

    System.out.println("Elapsed time regex (naive): " + elapsed + " ms");
    System.out.println("Doubles: " + naiveRegexCount);


    // reset the timer for the next run
    start = System.currentTimeMillis();

    int compiledRegexCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleCompiledRegex(values[i]) ) {
            compiledRegexCount++;
        }
    }

    end = System.currentTimeMillis();
    elapsed = end - start;

    System.out.println("Elapsed time regex (compiled): " + elapsed + " ms");
    System.out.println("Doubles: " + compiledRegexCount);
}


public static boolean isDoubleParse(String s) {
    if( s == null ) return false;
    try {
        Double.parseDouble(s);
        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}

public static boolean isDoubleScan(String s) {
    Scanner scanner = new Scanner(s);
    return scanner.hasNextDouble();
}

public static boolean isDoubleRegex(String s) {
    return s.matches(regExp);
}

public static boolean isDoubleCompiledRegex(String s) {
    Matcher m = pattern.matcher(s);
    return m.matches();
}

当我运行上面的代码时,我得到以下输出:

解析经过的时间:235 毫秒
双打:24966
扫描耗时:31358 ms
双打:24966
经过时间正则表达式(天真):1829 毫秒
双打:24966
经过时间正则表达式(已编译):109 毫秒
双打:24966

鉴于正则表达式的复杂性,正则表达式方法运行得相当快,但仍不如使用Double.parseDouble(s) 进行简单解析那么快。正如 cmets 中所指出的,有一些值(例如 NaN)可能不应该通过解析器。

更新:

按照@Gabe 的建议预编译正则表达式会有所不同。编译的正则表达式方法现在是明显的赢家。

【讨论】:

  • 你能用正则表达式方法(stackoverflow.com/questions/3133770/…)再次运行它吗?
  • @Gabe:当然。正则表达式示例不如Double.parseDouble(s) 示例快,但我不得不质疑该正则表达式的复杂性。 ;)
  • 是的,正则表达式有点复杂,但是当您从文档 (java.sun.com/j2se/1.5.0/docs/api/java/lang/Double.html) 中获取它时,它会更容易理解。它还使您可以灵活地消除诸如“NaN”之类的内容。
  • @Gabe:这是一个不错的功能。我会尝试简化它并使用保护子句捕捉其中一些特殊情况,但恐怕我已经进入“过早优化”领域。
  • 我怀疑如果您使用正则表达式执行Pattern.compile 并将其分配给静态变量,则模式匹配器不必每次都编译它,并且正则表达式版本会更快。当然,它可以在底层做一些编译模式缓存,所以它可能不会有什么不同。
【解决方案4】:

您可以创建一个Scanner(String) 并使用hasNextDouble() 方法。从它的javadoc:

如果此扫描器输入中的下一个标记可以是,则返回 true 使用解释为双精度值 nextDouble() 方法。扫描仪 不超过任何输入。

比如这个sn-p:

List<String> values = Arrays.asList("foo", "1", "2.3", "1f", "0.2d", "3.14");
for (String source : values) {
    Scanner scanner = new Scanner(source);
    System.out.println(String.format("%4s: %s", source, scanner.hasNextDouble()));
}

会产生以下输出:

富:假 1:真 2.3:真 1f:错误 0.2d:假 3.14:真

【讨论】:

  • 使用扫描仪比Double.parseDouble(s) 慢很多(数量级,否则我不会费心提及它)。也许您可以在下面查看我的代码并提出一种加快速度的方法。我知道每次都创建一个新的 Scanner 并不好。
  • @Bill 好吧,老实说,循环只是用于示例(要求尚不清楚)。不过,我没想到会有这样的差异。我去看看。
  • 我真的希望这也会更快。 Scanner 在解析之前检查输入,我认为这会给它一个优势,因为不会引发异常。
  • 感谢您的出色回答,非常有帮助。
【解决方案5】:
public boolean isDouble(String value) {
    try {
        Double.parseDouble(value);
        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}

【讨论】:

  • 如果您知道该值不能为空,那也没关系。 Null(与 Integer.parseInt 不同)抛出空指针异常,至少在我尝试过的 JDK 版本中是这样。
  • 空检查真的有那么难添加吗?
  • @Yishai:或者,OP 可能意味着存在一个实际的字符串,而不仅仅是一个字符串或 null 的东西。很难说,因为问题很模糊。
  • @ILMTitan,不,不是,如果你知道的话。 Integer.parseInt 的行为可能会误导您认为您不必这样做。
  • @Yishai OP提到“字符串中包含的值”,而不是可以为空的变量。你似乎在吹毛求疵。
【解决方案6】:

您可以使用 Apache Commons Lang 的 util 类:

NumberUtils.isNumber(aString);

它是 null 安全的,不需要使用 try-catch 块。

注意: 解析双精度,如果小数分隔符是点.

编辑: isNumber 已弃用,将从 Lang 4.0 中删除

更好用:

NumberUtils.isCreatable(aString);

【讨论】:

    【解决方案7】:

    你可以尝试用Double.parseDouble(String s)解析它

    如果解析成功,这将返回双精度值,如果不可解析,则返回异常。

    因此,您可以将整个内容包装在一个包含 try-catch 的函数中,如果遇到异常则返回 false,如果获得实际值则返回 true。

    【讨论】:

    • 如果值为整数,这也将返回 true
    • @akf 那是因为任何int 值也是double
    • 我同意,我想知道 OP 要测试的区别是否是输入值是双精度还是整数。
    【解决方案8】:

    我建议这样做:

    try {
      d = Double.parseDouble(myString);
    }
    catch (NumberFormatException ex) {
        // Do something smart here...
    }
    

    【讨论】:

      【解决方案9】:

      我们必须处理 NumberFormatException 和空指针异常来检查给定的 String 是数字还是字母数字

      public static boolean isNumeric(String strNum) {
              try {
                 Double.parseDouble(strNum);
              } catch (NumberFormatException | NullPointerException nfe) {
                  return false;
              }
              return true;
          }
      

      【讨论】:

        【解决方案10】:

        其他人推测您可能还想知道输入不是以整数表示的。根据您的要求,这可能会快速而肮脏地完成这项工作:

        public static void main(String[] args) throws Exception {
            System.out.println(isNonIntegerDouble("12"));  //false
            System.out.println(isNonIntegerDouble("12.1")); //true
            System.out.println(isNonIntegerDouble("12.0")); //true
        }
        
        public static boolean isNonIntegerDouble(String in) {
            try {
                Double.parseDouble(in);
            } catch (NumberFormatException nfe) {
                return false;
            }
            try {
                new BigInteger(in);
            } catch (NumberFormatException nfe) {
                return true;
            }
            return false;
        }
        

        在这一点上,我觉得字符串匹配会是一个更合适的选择。

        【讨论】:

          【解决方案11】:

          您可以在字符串上使用以下正则表达式:

          [-+]?[0-9]*\.?[0-9]*
          

          看看是否匹配。

          【讨论】:

          • 这个问题是它不允许国际化。小数点分隔符并不总是 \.
          【解决方案12】:

          我修改了 Jonas 的 isInteger() 方法,为我自己的项目提供了 isDecimal() 方法,我在下面列出了代码。

          可能有人可以更改添加更多代码来区分双精度和浮点数。

          它应该很快就出来了,而且可能比正则表达式更好,虽然我没有检查。唯一的缺陷是在溢出条件等情况下行为不端。

          请注意,我在 What's the best way to check to see if a String represents an integer in Java? 引用了 Bill 的帖子

              public boolean isDecimal(String str) {
                  if (str == null) {
                      return false;
                  }
                  int length = str.length();
                  if (length == 1 ) {
                      return false;
                  }
                  int i = 0;
                  if (str.charAt(0) == '-') {
                      if (length < 3) {
                          return false;
                      }
                      i = 1;
                  }
                  int numOfDot = 0;
                  for (; i < length; i++) {
                      char c = str.charAt(i);
                      if (c == '.')
                          numOfDot++;
                      else if (c == '/')
                          return false;
                      else if (c < '.' || c > '9') {
                          return false;
                      }
                  }
                  if (numOfDot != 1 )
                      return false;
          
                  return true;
              }
          

          【讨论】:

            【解决方案13】:

            如果你能找到一种方法将数字从字符串中分离出来,也许可以使用 split 方法。让我们说num[1] = 25; 然后你可以做这样的事情来检查它是否是双重的。

            boolean isDouble;
            if(num[1].contains(".")){
            isDouble = true;
            }
            else{
            isDouble = false;
            }
            

            【讨论】:

              【解决方案14】:
                  public boolean isDouble( String input )  
                  {  
                     try  
                     {  
                        Double.parseDouble( input );  
                        return true;  
                     }  
                     catch( Exception e)  
                     {  
                       return false;  
                    }  
                }  
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-11-27
                • 1970-01-01
                • 2021-02-26
                • 2014-10-07
                • 2023-03-09
                相关资源
                最近更新 更多