DecimalFormat 分组符号的不同表示

Posted

技术标签:

【中文标题】DecimalFormat 分组符号的不同表示【英文标题】:Different representations of DecimalFormat grouping symbol 【发布时间】:2020-06-05 07:09:44 【问题描述】:

今天我在下一个代码清单中发现了一个语言环境问题。我的目标是用瑞士德语语言环境(de-CH)格式化数字,以便用撇号(')分隔数千,用句点(.)分隔小数。根据ICU,这是该国家/地区语言环境的默认设置。

final NumberFormat format = NumberFormat.getNumberInstance(new Locale("en", "US"));
format.setMinimumFractionDigits(1);
format.setMaximumFractionDigits(1);

assertEquals("1'000.2", format.format(1000.2455));

这在我的同事使用瑞士德语语言环境的机器上可以正常工作。但是,当我在使用美国语言环境的机器上运行它时,测试失败,因为组分隔符是 ' 而不是 ' (apostrophes)。

expected:<1[']000.2> but was:<1[’]000.2>
Expected :1'000.2
Actual   :1’000.2

通过另一个SO post 我发现我可以访问和修改 DecimalFormat 以实现我的目标。虽然单元测试对这个结果很满意,但我并不满意。从DecimalFormat API 我不明白为什么分组符号偏离了预期的千位分隔符。因为它是同一个字符,所以我看到的唯一罪魁祸首是 IDE(或者更确切地说是它使用的字体)。

System.out.println(Integer.valueOf('’'));
39
System.out.println(Integer.valueOf('\''));
8217

我的问题是:如何编写单元测试,使其不易受到同一字符的不同表示的影响?

【问题讨论】:

你为什么要写JUnit测试来测试JDK的代码? 【参考方案1】:

总结

对于瑞士德语语言环境,使用(不是')作为千位分隔符的Java 输出在我看来是正确的。 ICU文档和Java也参考

鉴于此,您的单元测试不必关注千位分隔符的不同表示形式 - 除非您明确要使用 ',而不是

背景

两个不同的字符是:

' - 标准键盘撇号(Unicode U+0027) - 右单引号(Unicode U+2019)

根据问题中链接到的语言环境页面 (the de-CH locale page here),数字分组分隔符是 ' - 标准撇号。

但是,我认为此文档可能不正确 - 或者,至少是误导 - 在您的问题的上下文中。

Java Locale 对象从IANA Language Subtag Registry 获取其语言子标签值。

瑞士德语的注册表项是这样的:

Type: language
Subtag: gsw
Description: Swiss German
Description: Alemannic
Description: Alsatian
Added: 2006-03-08
Suppress-Script: Latn

因此,我们使用“gsw”为瑞士德语构建 Java 语言环境,如下所示:

Locale swissGermanLocale = new Locale("gsw");

de-CH 语言标签是创建“瑞士德语”标签的另一种方法 - 这是问题中引用的页面所引用的标签。

但 ICU “gsw” 语言环境也有 this other page。在this 页面上,分组分隔符是 右单引号。仅通过查看页面很难判断 - 但如果您复制/粘贴到等宽字体,您会看到差异。

Java 代码示例

看Java,我们可以这样写:

double d = 12345.67;
// This line is just so my console prints out the correct UTF-8 characters:
PrintStream out = new PrintStream(System.out, true, StandardCharsets.UTF_8);

Locale swissGermanLocale = new Locale("gsw");
//Locale swissGermanLocale = new Locale("de", "CH");
out.println(swissGermanLocale.getDisplayName());  // Swiss German

final NumberFormat gswFormat = NumberFormat.getNumberInstance(swissGermanLocale);

// Find out what the grouping separator is for the given locale:
DecimalFormat decimalFormat = (DecimalFormat)
NumberFormat.getNumberInstance(swissGermanLocale);
char c = decimalFormat.getDecimalFormatSymbols().getGroupingSeparator();
out.println(c);  // ’

out.println(gswFormat.format(d));  // 12’345.67

请注意,基于gsw 的语言环境称为“瑞士德语”。

基于de-CH 的语言环境称为“德语(瑞士)”。细微的差别。

无论如何,最终结果是数字使用 分隔符,而不是标准撇号。

如本答案开头所述,您可能希望/需要使用 ' 作为分隔符,作为对“官方”语言环境格式的有意识更改。如果是这样,那么您可以使用您提到的十进制格式对象。

【讨论】:

以上是关于DecimalFormat 分组符号的不同表示的主要内容,如果未能解决你的问题,请参考以下文章

DecimalFormat用法

DecimalFormat(数字格式)

java中 DecimalFormat格式的定义

java中 DecimalFormat格式的定义

关于DecimalFormat的用法

为啥 DecimalFormat 忽略分组字符?