312

Kotlin has an excellent feature called string templates.

val i = 10 
val s = "i = $i" // evaluates to "i = 10"

But is it possible to have any formatting in the templates? For example, I would like to format Double in string templates in kotlin, at least to set a number of digits after a decimal separator:

val pi = 3.14159265358979323
val s = "pi = $pi??" // How to make it "pi = 3.14"?
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
MajesticRa
  • 13,770
  • 12
  • 63
  • 77
  • 3
    Is there any multiplatform solution to this? – KenIchi Jan 21 '20 at 07:40
  • 1
    At least you can use `.format` as a workaround. Unfortunately in KMM, `String.format` is still not available, at least til' 2022. It's not in `stdlib.text` or `kotlinx`. String template is cool but C style formatting is fundamental, I assume the complexity is perhaps based on variant variable list, but it's still quite disappointing. – superarts.org Jan 01 '23 at 16:04
  • @superarts.org YEP! Ran into this exact thing just now! String.format isn't there in KMM! Amazing how much of Kotlin works in KMM so far, and it's mostly been an exercise of de-java-ifying it all, but things like String.format not there, the helL Also, Array.plus(element) isn't there, and Array.plus(Array) is there - inconsistent (oversight probably). – Mike Mar 11 '23 at 02:14

10 Answers10

364

Unfortunately, there's no built-in support for formatting in string templates yet, as a workaround, you can use something like:

"pi = ${pi.format(2)}"

the .format(n) function you'd need to define yourself as

fun Double.format(digits: Int) = "%.${digits}f".format(this)

This will work only in Kotlin/JVM.

There's clearly a piece of functionality here that is missing from Kotlin at the moment, we'll fix it.

cubuspl42
  • 7,833
  • 4
  • 41
  • 65
Andrey Breslav
  • 24,795
  • 10
  • 66
  • 61
192

As a workaround, There is a Kotlin stdlib function that can be used in a nice way and fully compatible with Java's String format (it's only a wrapper around Java's String.format())

See Kotlin's documentation

Your code would be:

val pi = 3.14159265358979323
val s = "pi = %.2f".format(pi)
William Desportes
  • 1,412
  • 1
  • 22
  • 31
akhy
  • 5,760
  • 6
  • 39
  • 60
  • 2
    I'm guessing he meant this documentation: https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.util.Locale-java.lang.String-java.lang.Object...- – stuckj Dec 27 '17 at 05:35
  • @Rob See also [discussion](https://stackoverflow.com/questions/23086291/format-in-kotlin-string-templates#comment79967924_33492636) on the documentation – Matt Mc Mar 09 '18 at 04:44
45

Kotlin's String class has a format function now, which internally uses Java's String.format method:

/**
 * Uses this string as a format string and returns a string obtained by substituting the specified arguments,
 * using the default locale.
 */
@kotlin.internal.InlineOnly
public inline fun String.Companion.format(format: String, vararg args: Any?): String = java.lang.String.format(format, *args)

Usage

val pi = 3.14159265358979323
val formatted = String.format("%.2f", pi) ;
println(formatted)
>>3.14
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
user1767754
  • 23,311
  • 18
  • 141
  • 164
  • 2
    String.Companion.format is not found now in Kotlin v1.2.21. What is the alternative?. – Sai Feb 03 '18 at 09:07
42

It's simple, use:

val str: String = "%.2f".format(3.14159)
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
masoomyf
  • 685
  • 8
  • 15
8

Since String.format is only an extension function (see here) which internally calls java.lang.String.format you could write your own extension function using Java's DecimalFormat if you need more flexibility:

fun Double.format(fracDigits: Int): String {
    val df = DecimalFormat()
    df.setMaximumFractionDigits(fracDigits)
    return df.format(this)
}

println(3.14159.format(2)) // 3.14
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
4

A couple of examples:

infix fun Double.f(fmt: String) = "%$fmt".format(this)
infix fun Double.f(fmt: Float) = "%${if (fmt < 1) fmt + 1 else fmt}f".format(this)

val pi = 3.14159265358979323

println("""pi = ${pi f ".2f"}""")
println("pi = ${pi f .2f}")

some_engineer
  • 1,844
  • 1
  • 19
  • 26
2

Kotlin 1.7.2

String's format method takes in a formatter-string and any number of args in nullable Any type.

fun String.Companion.format(format: String, vararg args: Any?) : String

Real code may look like this:

fun main() {
    
    val pi: Double = 3.141592653589
    val gravity: Double = 9.80665
    var str = String.Companion.format("%.2f %n%.2f", pi, gravity)
    
    println(str)
}

Where %n is a line separator, and %f is Float in Decimal Format.

Results are:

//    3.14 
//    9.81
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
0

It has string formatting Example in Kotlin for Android TextView:

val format = String.format("<font color=#3177a3> test1: <b>%s</b><br> test2: <b>%s</b><br> test3: <b>%s</b></font>", "value1", "value2", "value3")
textView.text = format
Deniz Babat
  • 205
  • 2
  • 6
0

As a workaround simple solution for multiplatform you can use:

import kotlin.math.abs
import kotlin.math.pow

fun Number.simpleFormat(numberDigitsAfterSeparator: Int = 0, decimalSeparator: Char = ','): String {
    if(numberDigitsAfterSeparator < 0)
        throw IllegalArgumentException("numberDigitsAfterSeparator should be >= 0 but is $numberDigitsAfterSeparator")

    val prefix = this.toInt()
    if(numberDigitsAfterSeparator == 0)return "$prefix"

    val sign = if(this.toDouble() >= 0.0) "" else "-"

    val afterSeparatorPart = abs(this.toDouble() - prefix)
    val suffixInt = (10.0.pow(numberDigitsAfterSeparator) * afterSeparatorPart).toInt()
    val suffix = if(afterSeparatorPart >= 1.0) "$suffixInt" else addNullsBefore(suffixInt, numberDigitsAfterSeparator)
    return "$sign${abs(prefix)}$decimalSeparator$suffix"
}

fun addNullsBefore(suffixInt: Int, numberDigitsAfterSeparator: Int): String {
    var s = "$suffixInt"
    val len = s.length
    repeat(numberDigitsAfterSeparator - len) { _ -> s = "0$s" }
    return s
}

Test:

fun main() {


    println((-0.00001).simpleFormat(5))
    println(3.1415927.simpleFormat(2))
    println(3.99.simpleFormat(2))
    println((-3.99).simpleFormat(2))

    println(3.1415927.simpleFormat())
    println(3.99.simpleFormat())
    println((-3.99).simpleFormat())

    println((-3.99).simpleFormat(-1))
}

wiht output:

-0,00001
3,14
3,99
-3,99
3
3
-3
Exception in thread "main" java.lang.IllegalArgumentException: numberDigitsAfterSeparator should be >= 0 but is -1..
Viktor Sirotin
  • 169
  • 1
  • 8
-5

It has string formatting Example:

fun printSum(a: Int, b: Int): Unit {
    println("sum of $a and $b is ${a + b}")
}
Sibyl
  • 134
  • 1
  • 8