JDK升级11后阿拉伯RTL场景NumberFormat负数解析异常问题
问题复现代码
Locale locale = new Locale("ar", "AE"); NumberFormat format = NumberFormat.getNumberInstance(locale); System.out.println(format.parse("55-"));
上述代码在JDK 8环境下执行返回-55,在JDK 11环境下执行返回55;同环境下调用format.parse("-55")方法会抛出ParseException。
版本行为差异的根本原因
核心差异来自JDK版本迭代中区域设置(Locale)数据源的默认规则切换:
- JDK 8及更早版本默认使用JRE自带的兼容型区域数据(COMPAT),这套数据对ar_AE(阿拉伯联合酋长国阿拉伯语)的数字解析规则做了非标准的宽松适配:既支持前缀ASCII减号识别负数,也错误兼容了后缀ASCII减号的写法,会把
"55-"解析为-55,不符合Unicode针对阿拉伯语RTL(从右到左)场景的官方本地化规范。 - 从JDK 9开始,CLDR(Unicode通用区域数据仓库)被设为最高优先级的区域数据来源,JDK 11绑定的是高版本CLDR规则,严格遵循ar_AE区域的数字格式标准:
- 阿拉伯语RTL场景下的标准负号不是ASCII编码的连字符
-(U+002D) - 默认解析模式下,遇到数字串尾部的非合法数字字符会直接截断,返回已解析完成的数字部分,因此
"55-"只会解析出前面的55,尾部的ASCII减号被直接忽略 - 如果把ASCII减号放在数字开头,因为起始字符不属于当前Locale下的合法数字起始字符,解析会直接中断抛出ParseException
- 阿拉伯语RTL场景下的标准负号不是ASCII编码的连字符
Java 11中正确解析阿拉伯语RTL场景负数的方案
根据业务兼容需求,可以选择以下三种方案:
- 方案1:遵循CLDR标准格式解析
ar_AE区域CLDR规范规定的标准负数,使用Unicode数学减号−(U+2212,注意和ASCII连字符区分)作为负号,逻辑字符串格式为−55,RTL渲染时负号会自动显示在数字的视觉左侧,直接用默认配置的NumberFormat解析该格式字符串即可得到正确的-55结果。 - 方案2:开启宽松解析兼容旧写法
如果业务需要兼容"55-"、"-55"这类ASCII减号的旧写法,手动调用NumberFormat的setLenient(true)方法开启宽松解析即可,示例代码:Locale locale = new Locale("ar", "AE"); NumberFormat format = NumberFormat.getNumberInstance(locale); // 开启宽松解析模式 format.setLenient(true); System.out.println(format.parse("55-")); // 输出-55 System.out.println(format.parse("-55")); // 输出-55 - 方案3:全局切换回JDK8兼容数据源
如果需要全局复用JDK8的解析行为,不需要修改业务代码,直接在JVM启动参数中添加如下配置,将JDK自带的兼容型区域数据优先级调到CLDR之前即可:-Djava.locale.providers=COMPAT,CLDR
内容的提问来源于stack exchange,提问作者igce
相关产品推荐
相关产品推荐

