用逗号作为小数分隔符解析Double的最佳方法?

Posted

技术标签:

【中文标题】用逗号作为小数分隔符解析Double的最佳方法?【英文标题】:Best way to parseDouble with comma as decimal separator? 【发布时间】: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 不,格式没有错。某些语言环境使用逗号作为千位分隔符,因此您可以在一个数字中包含多个逗号,并且从技术上讲它仍然是有效的输入。 【参考方案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();

【讨论】:

这个策略在这里详细解释:ibm.com/developerworks/library/j-numberformat【参考方案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://***.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 有很大不同,请解释原因。

以上是关于用逗号作为小数分隔符解析Double的最佳方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何禁止逗号作为小数分隔符

解析逗号分隔列表的最佳方法

用 C 风格在 Java 中解析?

用逗号作为小数分隔符的数字的 Google 表单正则表达式

JAVA编程:输出值要求格式化为:以逗号作为每千位的间隔符,并且小数点后四舍五入为两位。例如:123.45

Dygraphs,逗号作为小数分隔符