You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于指定动态模式格式化数字的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"
    }

问题分析与修复方案

原代码的核心问题:

  1. 千位分隔符仅执行单次替换(replaceFirst),导致整数部分只有第一个分组被插入分隔符
  2. 未适配不同模式下的千位分隔符、小数点符号差异
  3. 小数部分处理逻辑复杂且存在精度隐患

修复后的代码将自动适配不同模式,正确处理数字分组:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 08:30:49