如何让标准Kotlin与Android Kotlin解析畸形数字结果一致?
解决标准Kotlin与Android Kotlin中NumberFormat解析畸形数字的一致性问题
问题背景
使用java.text.NumberFormat解析畸形数字字符串(如"1,1.90.00")时,出现跨平台解析结果不一致的问题:
- 标准Kotlin环境中,解析结果为
11.90 - Android Kotlin环境中,解析结果为
1
该差异导致单元测试与仪器化测试结果不统一,且代码用于用户输入校验/修正场景,无法控制用户输入内容。
差异原因
标准JDK的NumberFormat实现与Android基于ICU的NumberFormat实现,对畸形输入的容错处理逻辑不同:
- 标准JDK会尝试跳过无效分隔符,尽可能解析出合理数值
- Android的ICU实现遇到第一个非法分隔符后就停止解析
解决方案:自定义统一解析逻辑
通过自定义字符串清理+解析逻辑,绕过系统NumberFormat的平台差异,确保跨平台解析结果一致。
实现代码
import java.text.DecimalFormatSymbols import java.text.NumberFormat import java.text.ParseException import java.util.Locale fun parseMalformedNumber(input: String, locale: Locale): Number? { val decimalSeparator = DecimalFormatSymbols.getInstance(locale).decimalSeparator val groupingSeparator = DecimalFormatSymbols.getInstance(locale).groupingSeparator // 移除所有千分位分组符(如美式Locale的逗号) var cleanedInput = input.replace(groupingSeparator.toString(), "") // 仅保留第一个小数点,后续的小数点全部移除 val firstDecimalPos = cleanedInput.indexOf(decimalSeparator) if (firstDecimalPos != -1) { cleanedInput = buildString { append(cleanedInput.substring(0, firstDecimalPos + 1)) append(cleanedInput.substring(firstDecimalPos + 1).replace(decimalSeparator.toString(), "")) } } // 清理所有非数字、非小数点的残留字符 cleanedInput = cleanedInput.replace(Regex("[^\\d$decimalSeparator]"), "") return try { NumberFormat.getInstance(locale).parse(cleanedInput) } catch (e: ParseException) { null // 处理完全无法解析的无效输入 } }
使用示例
// 测试畸形输入"1,1.90.00" val value = "1,1.90.00" val parsed = parseMalformedNumber(value, Locale.US) // 标准Kotlin与Android环境下,解析结果均为11.9000
逻辑说明
- 移除分组符:千分位分组符(如美式的逗号)仅用于格式化显示,不影响数值本身,直接移除
- 保留单个小数点:只保留第一个出现的小数点,后续的小数点视为无效输入移除
- 清理残留非法字符:确保最终输入仅包含数字和单个小数点
- 统一解析:使用系统
NumberFormat处理清理后的合法字符串,保证跨平台结果一致
扩展调整
可根据实际业务需求调整清理规则:
- 若允许负号,可在正则表达式中添加
-,改为[^\\d$decimalSeparator-] - 若需要保留特定格式的输入,可调整分组符和小数点的处理逻辑
内容的提问来源于stack exchange,提问作者shilomig
相关产品推荐
相关产品推荐

