【问题标题】:Best way to parseDouble with comma as decimal separator?用逗号作为小数分隔符解析Double的最佳方法?
【发布时间】:2011-05-18 10:32:43
【问题描述】:

由于comma 用作decimal separator,此代码抛出NumberFormatException

String p="1,234";
Double d=Double.valueOf(p); 
System.out.println(d);

有没有比p = p.replaceAll(",","."); 更好的方法来解析"1,234" 以获得1.234

【问题讨论】:

  • 根据我的经验,按照您的建议,replaceAll() 是最好的方法。它不依赖于当前的语言环境,它很简单,而且很有效。
  • @Marco Altieri:replaceAll(",",".") 用点替换所有逗号。如果没有逗号,那么它什么也不做。 Double.valueOf() (仅)适用于使用点作为小数分隔符的字符串。当前默认语言环境不影响此处的任何内容。 docs.oracle.com/javase/8/docs/api/java/lang/…
  • replaceAll(",",".") 的唯一问题是它只有在有一个逗号时才有效:即:1,234,567 将抛出 java.lang.NumberFormatException: multiple points。具有正向前瞻的正则表达式就足够了 p.replaceAll(",(?=[0-9]+,)", "").replaceAll(",", ".") 更多信息:regular-expressions.info/lookaround.html
  • 没有问题。 NumberFormatException 很好。你怎么知道哪个逗号是正确的?格式错误,您所能做的就是向用户显示比异常更易读的消息。
  • @TheincredibleJan 不,格式没有错。某些语言环境使用逗号作为千位分隔符,因此您可以在一个数字中包含多个逗号,并且从技术上讲它仍然是有效的输入。

标签: java


【解决方案1】:

使用java.text.NumberFormat:

NumberFormat format = NumberFormat.getInstance(Locale.FRANCE);
Number number = format.parse("1,234");
double d = number.doubleValue();

更新:

要支持多语言应用,请使用:

NumberFormat format = NumberFormat.getInstance(Locale.getDefault());

【讨论】:

  • 这仅适用于当前默认语言环境碰巧使用逗号作为小数分隔符的情况。
  • 为了进一步搞砸,一些语言环境使用逗号作为 千位分隔符,在这种情况下,“1,234”将解析为 1234.0 而不是引发错误。
  • NumberFormat 的问题在于它会默默地忽略无效字符。因此,如果您尝试解析“1,23abc”,它会很高兴地返回 1.23,而不会告诉您传入的字符串包含不可解析的字符。在某些情况下,这实际上可能是理想的,但我认为这通常不是理想的行为。
  • 对于土耳其,您应该使用 NumberFormat.getInstance(new Locale(tr_TR))
  • 谁使用什么分隔符见en.wikipedia.org/w/…
【解决方案2】:

您可以使用它(法语语言环境有 , 用于小数分隔符)

NumberFormat nf = NumberFormat.getInstance(Locale.FRANCE);
nf.parse(p);

或者您可以使用java.text.DecimalFormat 并设置相应的符号:

DecimalFormat df = new DecimalFormat();
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator(',');
symbols.setGroupingSeparator(' ');
df.setDecimalFormatSymbols(symbols);
df.parse(p);

【讨论】:

  • 是...如果我们不设置千位分组分隔符而只使用法语格式,西班牙格式的数字(1.222.222,33)将转换为“1 222 222,33 ",这不是我想要的。所以谢谢!
  • 另一件事是,西班牙语言环境未列为“默认”,我无法使用new Locale("es", "ES") 构造正确格式的Locale,然后使用NumberFormat 自动解析数字字符串,使用@ 987654329@ 作为小数分隔符,. 作为千位分隔符。只有 DecimalFormat 有效。
  • 为什么不是所有国家都可用?我对使用法语语言环境来格式化波兰数字感到很奇怪......
【解决方案3】:

正如 E-Riz 指出的,NumberFormat.parse(String) 将“1,23abc”解析为 1.23。要获取整个输入,我们可以使用:

public double parseDecimal(String input) throws ParseException{
  NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.getDefault());
  ParsePosition parsePosition = new ParsePosition(0);
  Number number = numberFormat.parse(input, parsePosition);

  if(parsePosition.getIndex() != input.length()){
    throw new ParseException("Invalid input", parsePosition.getIndex());
  }

  return number.doubleValue();
}

【讨论】:

【解决方案4】:
Double.parseDouble(p.replace(',','.'))

... 非常快,因为它逐个字符地搜索底层字符数组。字符串替换版本编译一个正则表达式来评估。

基本上 replace(char,char) 大约快 10 倍,并且由于您将在低级代码中执行此类操作,因此考虑这一点是有意义的。热点优化器无法解决...我的系统上肯定没有。

【讨论】:

    【解决方案5】:

    如果您不知道正确的语言环境并且字符串可以有千位分隔符,这可能是最后的手段:

        doubleStrIn = doubleStrIn.replaceAll("[^\\d,\\.]++", "");
        if (doubleStrIn.matches(".+\\.\\d+,\\d+$"))
            return Double.parseDouble(doubleStrIn.replaceAll("\\.", "").replaceAll(",", "."));
        if (doubleStrIn.matches(".+,\\d+\\.\\d+$"))
            return Double.parseDouble(doubleStrIn.replaceAll(",", ""));
        return Double.parseDouble(doubleStrIn.replaceAll(",", "."));
    

    请注意:这将愉快地解析诸如“R 1 52.43,2”到“15243.2”之类的字符串。

    【讨论】:

      【解决方案6】:

      这是我在自己的代码中使用的静态方法:

      public static double sGetDecimalStringAnyLocaleAsDouble (String value) {
      
          if (value == null) {
              Log.e("CORE", "Null value!");
              return 0.0;
          }
      
          Locale theLocale = Locale.getDefault();
          NumberFormat numberFormat = DecimalFormat.getInstance(theLocale);
          Number theNumber;
          try {
              theNumber = numberFormat.parse(value);
              return theNumber.doubleValue();
          } catch (ParseException e) {
              // The string value might be either 99.99 or 99,99, depending on Locale.
              // We can deal with this safely, by forcing to be a point for the decimal separator, and then using Double.valueOf ...
              //http://stackoverflow.com/questions/4323599/best-way-to-parsedouble-with-comma-as-decimal-separator
              String valueWithDot = value.replaceAll(",",".");
      
              try {
                return Double.valueOf(valueWithDot);
              } catch (NumberFormatException e2)  {
                  // This happens if we're trying (say) to parse a string that isn't a number, as though it were a number!
                  // If this happens, it should only be due to application logic problems.
                  // In this case, the safest thing to do is return 0, having first fired-off a log warning.
                  Log.w("CORE", "Warning: Value is not a number" + value);
                  return 0.0;
              }
          }
      }
      

      【讨论】:

      • 如果默认语言环境是德语,逗号表示小数位怎么办?您可以传入,例如“1,000,000”,它不会解析为德语区域设置,然后将被替换为“1.000.000”,它不是有效的 Double。
      • 嗨@jimmycar,我刚刚更新了我的答案以使用我的静态方法的当前版本。我希望这能解决你的问题!皮特
      【解决方案7】:

      在 Kotlin 中,您可以使用如下扩展:

      fun String.toDoubleEx() : Double {
         val decimalSymbol = DecimalFormatSymbols.getInstance().decimalSeparator
        return if (decimalSymbol == ',') {
            this.replace(decimalSymbol, '.').toDouble()
        } else {
            this.toDouble()
        }
      }
      

      您可以在代码中的任何地方使用它,如下所示:

      val myNumber1 = "5,2"
      val myNumber2 = "6.7"
      
      val myNum1 = myNumber1.toDoubleEx()
      val myNum2 = myNumber2.toDoubleEx()
      

      简单且通用!

      【讨论】:

        【解决方案8】:

        您当然需要使用正确的语言环境。 This 问题会有所帮助。

        【讨论】:

          【解决方案9】:

          如果您不知道接收到的字符串值的语言环境,并且它不一定与当前默认语言环境相同,您可以使用:

          private static double parseDouble(String price){
              String parsedStringDouble;
              if (price.contains(",") && price.contains(".")){
                  int indexOfComma = price.indexOf(",");
                  int indexOfDot = price.indexOf(".");
                  String beforeDigitSeparator;
                  String afterDigitSeparator;
                  if (indexOfComma < indexOfDot){
                      String[] splittedNumber = price.split("\\.");
                      beforeDigitSeparator = splittedNumber[0];
                      afterDigitSeparator = splittedNumber[1];
                  }
                  else {
                      String[] splittedNumber = price.split(",");
                      beforeDigitSeparator = splittedNumber[0];
                      afterDigitSeparator = splittedNumber[1];
                  }
                  beforeDigitSeparator = beforeDigitSeparator.replace(",", "").replace(".", "");
                  parsedStringDouble = beforeDigitSeparator+"."+afterDigitSeparator;
              }
              else {
                  parsedStringDouble = price.replace(",", "");
              }
          
              return Double.parseDouble(parsedStringDouble);
          
          }
          

          无论字符串的语言环境是什么,它都会返回一个双精度值。无论有多少逗号或点。因此,通过1,000,000.54 将起作用,1.000.000,54 也将起作用,因此您不必再依赖默认语言环境来解析字符串。该代码没有尽可能优化,因此欢迎提出任何建议。我尝试测试大多数情况以确保它解决了问题,但我不确定它是否涵盖了所有情况。如果你找到一个突破值,请告诉我。

          【讨论】:

            【解决方案10】:

            这样就可以了:

            Double.parseDouble(p.replace(',','.')); 
            

            【讨论】:

            • 最初的问题说“有没有更好的方法来解析“1,234”得到1.234而不是:p = p.replaceAll(",".");",如果您认为replace 与使用replaceAll 有很大不同,请解释原因。
            猜你喜欢
            • 1970-01-01
            • 2012-12-24
            • 1970-01-01
            • 2013-07-11
            • 1970-01-01
            • 1970-01-01
            • 2014-11-27
            • 1970-01-01
            相关资源
            最近更新 更多