基于指定动态模式格式化数字的Kotlin实现问题求助
动态数字格式化适配自定义模式的问题
需要实现根据自定义模式动态格式化数字,例如:
- 数字
12038902.9使用模式"##,###,###.##"应输出12,038,902.9 - 同时要支持
"##.###.###,##"这类小数点和千位分隔符位置相反的模式
自行编写的Kotlin代码运行后得到错误输出 12,038902.9,尝试的代码如下:
fun main() { val number = 12038902.90 val pattern = "##,###,###.##" val formattedNumber = formatNumber(number, pattern) println(formattedNumber) } fun formatNumber(number: Double, pattern: String): String { val parts = pattern.split(".") val integerPart = parts[0].replace(",", "") val decimalPartFormat = parts[1] val intPartFormat = "%d" val decimalPartForm = "%.${decimalPartFormat.length}f" val formattedIntPart = String.format(intPartFormat, number.toInt()).replaceFirst("(?<=\\d)(?=(\\d{3})+(?!\\d))".toRegex(), ",") val decimalPart = (number * 10.0.pow(decimalPartFormat.length)).toInt() % 10.0.pow(decimalPartFormat.length).toInt() val formattedDecimalPart = String.format("%0${decimalPartFormat.length}d", decimalPart).replaceFirst("0*$".toRegex(), "").replaceFirst("(?<=\\d)(?=(\\d{3})+(?!\\d))".toRegex(), ",") return "$formattedIntPart.$formattedDecimalPart" }
问题分析与修复方案
原代码的核心问题:
- 千位分隔符仅执行单次替换(
replaceFirst),导致整数部分只有第一个分组被插入分隔符 - 未适配不同模式下的千位分隔符、小数点符号差异
- 小数部分处理逻辑复杂且存在精度隐患
修复后的代码将自动适配不同模式,正确处理数字分组:
fun main() { val number = 12038902.90 val pattern1 = "##,###,###.##" println(formatNumber(number, pattern1)) // 输出 12,038,902.9 val pattern2 = "##.###.###,##" println(formatNumber(number, pattern2)) // 输出 12.038.902,9 } fun formatNumber(number: Double, pattern: String): String { // 识别小数分隔符:模式中最后出现的非#符号 val decimalSeparator = pattern.first { it != '#' } // 拆分整数与小数部分的模式 val (integerPattern, decimalPattern) = pattern.split(decimalSeparator, limit = 2) // 识别千位分隔符:整数模式中的非#符号(无则为空) val thousandSeparator = integerPattern.firstOrNull { it != '#' } ?: "" // 处理整数部分:从右往左每3位插入千位分隔符 val intPart = number.toLong() val reversedInt = intPart.toString().reversed() val groupedInt = reversedInt.chunked(3).joinToString(thousandSeparator).reversed() // 处理小数部分:保留指定位数,去除末尾无意义的0 val decimalDigits = decimalPattern.count { it == '#' } val decimalPart = if (decimalDigits > 0) { val scaledValue = (number * 10.0.pow(decimalDigits)).toLong() % 10.0.pow(decimalDigits).toLong() scaledValue.toString().padStart(decimalDigits, '0').trimEnd('0') } else { "" } // 拼接最终结果 return if (decimalPart.isNotEmpty()) { "$groupedInt$decimalSeparator$decimalPart" } else { groupedInt } }
关键处理逻辑说明
- 自动识别分隔符:通过模式中的
#以外的字符,区分千位分隔符与小数分隔符,适配不同地区格式 - 整数分组优化:将数字字符串反转后每3位分组,再反转回来,确保所有分组都能正确插入分隔符
- 小数部分简化:按模式指定位数缩放数字,仅保留有效小数位,避免无效的末尾0
- 兼容多模式:无论模式是逗号作为千位分隔符、点作为小数点,还是反过来的格式,都能正确处理
内容的提问来源于stack exchange,提问作者Sumit Shukla
相关产品推荐
相关产品推荐

