Java 中非默认语言环境的 NumberFormat 解析不正确

Posted

技术标签:

【中文标题】Java 中非默认语言环境的 NumberFormat 解析不正确【英文标题】:NumberFormat incorrect parse for non default locale in java 【发布时间】:2017-10-05 05:21:12 【问题描述】:

NumberFormat 错误地解析了默认 en_US 以外的区域设置的数字。例如,对 fr_CA 的解析给出了不正确的结果。

NumberFormat nf = NumberFormat.getNumberInstance(new Locale("fr_CA"));
Number num = nf.parse("2.302,52");

输出为:2.302

预期输出:2302,522302.52

基本上是把这个数字的值从 2000 变成了 2,这是不正确的。

详情请看以下截图:

我无法识别出了什么问题,请帮忙!

【问题讨论】:

@Jens:一定要注意这个值不对,必须是2302.52,内部表示不能改变值! @Jens:既然你已经删除了你的评论,你能不能也修改一下你的反对票。 【参考方案1】:

编辑 1:

通过将小数点和分组分隔符设置为,我能够使用 DecimalFormat 正确解析非默认语言环境值:

        /**
         * Testing DF parse
         */
        DecimalFormat df = new DecimalFormat();
        DecimalFormatSymbols symbols = new DecimalFormatSymbols();
        symbols.setDecimalSeparator(',');
        symbols.setGroupingSeparator('.');
        df.setDecimalFormatSymbols(symbols);
        Number num = df.parse("2.302,52"); // OUTPUT is: 2302.52 -> correctly parsed

        /**
         * Round off and format
         */
        double d = num.doubleValue();
        double roundedVal = Math.round(d); // OUTPUT is: 2303.0 -> correctly rounded off
        String localeAwareVlue = df.format(roundedVal); // OUTPUT is: 2.303 -> using correct grouping separator
        valueString = localeAwareVlue;

但我真的希望能够在运行时动态地执行此操作,而不必为我可能收到的每种语言/国家/地区组合设置分隔符。请提供任何建议。

编辑 2:

我最终得到了以下最终代码,以便能够动态解析和格式化本地化值。这适用于 ###,###.## 和 ###.###,## 等模式 - 假设是 -

    收到的数据总是只有 2 位小数 小数分隔符可以是逗号或句点

    分组分隔符可以是句点或逗号

    String localValue = args.get(0).toString();
    // String locale = args.get(1).toString();
    
    char decChar = localValue.charAt(localValue.length()-3);
    char grpChar = ',';
    if(decChar == ',') 
        grpChar = '.';
    
    DecimalFormat df = new DecimalFormat();
    DecimalFormatSymbols symbols = new DecimalFormatSymbols();
    symbols.setDecimalSeparator(decChar);
    symbols.setGroupingSeparator(grpChar);
    df.setDecimalFormatSymbols(symbols);
    
    Number parsedValue = 0;
    try 
        parsedValue = df.parse(localValue);
    
    catch (ParseException pe) 
        pe.printStackTrace();
    
    
    /**
     * Round off and format to localize
     */
    double roundedValue = Math.round(parsedValue.doubleValue());
    String localeAwareValue = df.format(roundedValue);
    
    
    // TODO - use Locale with nf instead of above method
    // currently facing issue with incorrect parse for non default locale
    /*
       NumberFormat nf = NumberFormat.getInstance(new Locale(locale));
       Number num = nf.parse(localValue);
    */
    

不用说我对解决方案不满意,这更像是一种解决方法,直到我得到主要问题的解决方案 - NumberFormat 对非默认语言环境的错误解析。因此,仍然欢迎任何提示、建议和解决方案。

【讨论】:

【参考方案2】:

这里有两个问题,

1) 您创建的语言环境不正确。使用Locale.CANADA_FRENCH

2) 给定数字在默认和加拿大语言环境中均无效。根据ICU,在fr_CA中,分组分隔符为空,小数分隔符为,

因此,在您的示例中,它使用默认语言环境,它将. 视为小数分隔符并在, 处保释,而在正确的语言环境中,它将在. 处保释并简单地返回@987654328 @。

fr_CA 中写入这个数字的正确方式是2302,52

所以,真的,Java 是正确的,但你对这个数字格式的假设是不正确的。

【讨论】:

以上是关于Java 中非默认语言环境的 NumberFormat 解析不正确的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JVM 中设置默认语言环境?

Sonar以Sonar默认语言环境之外的其他语言显示违规消息

试验局ReentrantLock中非公平锁与公平锁的性能测试

在 Java 中的外国语言环境中格式化货币

Java学习随笔之一: Java 语言概述和开发环境

options:R语言环境变量设置