Master-slave
发布于 2019-10-28 / 14 阅读
0
0

Locale导致的DecimalFormat执行结果不一致问题

前言

调试软件bug的过程中发现通过DecimalFormat.format()方法返回的字符串无法通过new BigDecimal()解析,在产品环境和开发环境之间来来回回加打印测试最后定位到是Windows系统下的Locale变化导致的。

问题描述

DecimalFormat decimalFormatter = new DecimalFormat("0.0");
String decimalStr = decimalFormatter.format("0.0");

上述代码在开发环境(Windows中文版)中执行结果为0.0 但在产品运行环境(Windows英文版)中执行结果为0,0,该行为导致了在使用该Formatter格式化后的字符串(含逗号的)无法直接通过new BigDecimal转化为数值使用。

Locale

A Locale object represents a specific geographical, political, or cultural region. An operation that requires a Locale to perform its task is called locale-sensitive and uses the Locale to tailor information for the user. For example, displaying a number is a locale-sensitive operation— the number should be formatted according to the customs and conventions of the user's native country, region, or culture.

在Java中,Locale类用来区分一些地域敏感的信息,例如字符集,国家编码,数字的格式等信息。而Locale的值在默认情况下由JVM在启动时检测系统环境获取,所以在不同的环境中也就会有不同的Locale值,这在开发环境和产品环境就可能造成困扰。

解决方法

既然运行结果的差异是由Locale导致的,所以在编码过程中需要屏蔽掉其带来的数字格式化时的差异。

public static void printDiffernece(Locale local){
    NumberFormat nf = NumberFormat.getNumberInstance(local);
    DecimalFormat df = (DecimalFormat)nf;
    df.applyPattern("###,###.###");
    String output = df.format(123456.789);
    System.out.println("###,###.### " + output + " " + local.toString());
}
​
/*
JDK 1.8_131_32运行结果如下
  pattern       value          locale
###,###.###      123,456.789     Locale.US
###,###.###      123.456,789     Locale.GERMANY
###,###.###      123?456,789     Locale.FRANCE
*/

可以看到在不同的Locale下,使用相同的格式和数值,格式化后的结果是不一样的,而这就是问题的所在。

通过在代码中统一使用Locale.CHINA起到屏蔽JVM使用自动检测到的Locale的效果,达到结果的一致性。

DecimalFormat decimalFormatter = (DecimalFormat) NumberFormat.getInstance(Locale.CHINA);
decimalFormatter.applyPattern("0.0");
​

以后在编码需要格式化Number的时候,需要将国际化也考虑进来。

参考

Customizing Formats


评论