String formatting is a critical concept in Kotlin that enables constructing dynamic string outputs in a readable and reusable way. This guide will dive into the various methods for formatting strings through code examples.

We will specifically explore:

  • Foundational formatting syntax
  • Number and date formatting
  • Performance optimization
  • Advanced templating
  • Custom extensions
  • Regex-powered manipulation
  • JSON and HTML integration
  • Building template engines

Let‘s get started!

Introduction to Kotlin String Formatting

First, let‘s quickly understand the need for formatting strings programmatically.

Hardcoding text strings directly into applications leads to tight coupling and maintainability issues. What if you need to support multiple languages or build a dynamic user interface?

This is where string formatting helps. It allows injecting values into predefined string templates dynamically.

For instance, instead of:

"Welcome back John!" 

You can write:

"Welcome back ${user}!"

Here ${user} gets replaced with a name dynamically.

Common use cases include:

  • Constructing log messages
  • Building reusable UI text
  • Displaying numbers and dates
  • Generating files from templates
  • Producing JSON/HTML strings
  • Safe text interpolation

Kotlin provides several approaches to format strings flexibly which we will now explore.

Foundational String Interpolation

The simplest way is using string interpolation with ${} placeholders:

val user = "John"
println("Hello $user!") // Hello John!

Any valid expression can be embedded inside ${}:

println("Current year is ${Calendar.getInstance().get(Calendar.YEAR)}") // 2023

You can even insert function calls and logic flows:

if (user.isEmpty()) {
   println("Welcome guest!") 
} else {
   println("Welcome back ${formatName(user)}!")  
}

This keeps text separate from logic. But the syntax lacks fine-grained control.

Precision Formatting Specifiers

For numbers, dates etc. format specifiers like %d and %f help:

val price = 29.95
println("Price: %.2f" format(price)) // Price: 29.95 

println("ID: %05d".format(25)) // ID: 00025 

Some common specifiers are:

  • %s – String
  • %d – Integer
  • %f – Floating point number
  • %e – Exponential float
  • %x – Hexadecimal
  • %c – Character

You can also left/right align, truncate and pad strings:

println("| %-15s |".format("Name")) // | Name               |

Overall, basic interpolation and % specifiers cover many simple to moderate string building needs.

But there are more advanced approaches available…

Building Strings Lazily Using Templates

String templates provide a productive way to construct strings with dynamic data.

Unlike substitution, templates allow you to execute logic while appending text through a StringBuilder:

val result = buildString {
    append("Start of text\n")

    for (i in 1..5) {
        append(randomParagraph()) 
        append("\n")
    }

    append("End of text")
} 

println(result)

Here buildString {} allows appending text within a block. The string gets rendered lazily only when result is used.

Benefits include:

  • No need to declare temporary strings
  • Efficient buffer allocation
  • Cleaner logic flow
  • Lazily computed
  • Can add newlines easily

This approach shines for building JSON, XML, log files and other structured text outputs.

Optimizing String Performance

Let‘s now see some best practices around performance optimization with strings:

Use StringBuilder for heavy concatenations

// BAD
var sql = "SELECT * FROM " 
sql += "users WHERE "
sql += "age > 20"

// GOOD 
val sql = StringBuilder().append("SELECT * FROM ")
                       .append("users WHERE ")
                       .append("age > 20")

Overusing += causes repeated string allocations under the hood. StringBuilder helps reuse buffer space efficiently.

Extract constants as resources

Hardcoded strings bloat app size. Store them as constants:

const val USER_TEXT = "Welcome back {0}!" 

println(USER_TEXT format user)   

This allows easy reusability, localization and size optimization.

Enable optimizer flags

Use compiler flags like -opt-in=kotlin.RequiresOptIn to activate advanced optimizations and inline string operations.

Adopting these best practices ensures your formatter logic remains fast and lean.

Now let‘s tackle some real-world formatting challenges…

Formatting Numbers, Percentages, Times, Dates etc.

The kotlinx.datetime library contains handy extensions for formattingtemporal data types like dates, intervals, datetimes etc.

For instance, dates can be formatted in ISO8601, RFC3339, UI friendly strings and more:

import kotlinx.datetime.*

val now = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault())

println(now.toIsoString()) 

// 2023-02-11T14:23:41.345+05:30

println(now.format(DateTimeFormatter.ofPattern("MMM dd, yyyy")))

// Feb 11, 2023 

Similarly, times and intervals can be formatted as required:

val duration = 2.hours + 35.minutes
println(duration.formatIsoHours()) // PT2H35M

For number formatting, the kotlin.text library helps:

import kotlin.text.*

val pi = PI // 3.1415926535
println(pi.format(5)) // 3.14159

val miles = 1.25
println(miles.format("%.2f mi")) // 1.25 mi

You can format percentages, currency etc. in locales like French, Japanese easily:

val sales = 2342
val goal = 3000 

print(getPercent(sales, goal)) // 78.1%

fun getPercent(num: Int, outOf: Int) = 
    ((num.toDouble() / outOf.toDouble()) * 100.0)
     .format("%.1f", Locale.FRENCH) + "%"

So you can use domain libraries like kotlinx.datetime and kotlin.text for handling common data type formats effectively.

Going Further with Advanced Templating

Now let‘s push Kotlin templating to the next level using some custom extensions…

Safely inject values

Injecting raw user input into strings risks XSS vulnerabilities:

println("Welcome ${params.name}!") // Unsafe!!

Implement a safe interpolator instead:

suspend fun TemplateContext.safeInject(value: String) {
    append(encodeHtml(value)) 
}

// Usage:
buildString { 
    append("Welcome ")
    safeInject(input) // no vulnerabilities!
}

Reuse boilerplate markup

Repeatedly appending the same HTML/XML tags gets verbose:

buildString {
    append("<ul>\n")

    for(item in items) {
        append("   <li>$item</li>\n") 
    }

    append("</ul>")
}

Extract a render function instead:

fun TemplateBuilder.renderList(
  items: List<String>, 
  itemTag: String = "li"  
) {
    append("<ul>\n")

    for(item in items) {
        append("  <$itemTag>$item</$itemTag>\n")
    }

    append("</ul>\n")
}

// Usage: 
buildString {
    //...
    renderList(items, "item") 
} 

This keeps templates DRY. You can build your own tag library on top of the formatter.

Manipulating Strings using Regex

The Regex class contains advanced helpers for text manipulation:

val regex = "\\{(.*?)\\}".toRegex() 

val str = "{first_name} {last_name} joined {company}"

println( regex.replace(str) { 
    when (it.groups[1]!!.value) {
        "first_name" -> "John" 
        "last_name" -> "Smith"
        else -> "Acme"
    }
})

// John Smith joined Acme

Here capture groups extract matched text, allowing programmatic replacement with values.

Other useful methods are:

  • split() – Split on pattern
  • matches() – Check full string match
  • find() – Search for matchup
  • groupBy() – Group all matches

Overall Regex enables search-replace operations on strings robustly.

Integrating Strings with JSON and HTML

String templating works nicely for producing JSON content:

import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.*

data class User(val name: String, val age: Int)

fun main() {
    val user = User("John", 32)  

    val json = buildString {
        append(Json.encodeToString(user)) 
    }

    println(json)
    // {"name":"John","age":32}
}   

Here instead of parsing stringified JSON, you can encode objects directly into string outputs using kotlinx.serialization.

For HTML generation, Kotlinx HTML provides type-safe builders:

import kotlinx.html.*

fun main() { 
    val html = buildString { 
        appendHTML().html {
            head {
                title("My Page")
            }
            body { 
                h1 { +"Welcome!" }
                p { 
                    +"This bit of HTML was generated by Kotlin code!" 
                }
            }
        }
    }

    println(html)
}

The compiled HTML gets directly appended to the string output.

So templating integrates well with Kotlin‘s rich ecosystem libraries.

Building Your Own Template Engine

The string formatting APIs can form the foundation for building full-blown templating engines.

Let‘s design a simple example:

import kotlin.text.StringBuilder

class Templater {

    fun buildString(builder: TemplateBuilder.() -> Unit): String {
        val templateBuilder = TemplateBuilder()
        builder(templateBuilder)  
        return templateBuilder.toString()
    }
}

class TemplateBuilder : StringBuilder() {

    fun inject(value: Any?) {
        append(value) 
    }

    fun title(str: String) {
        append("")
    }

    // more tag functions...
}

fun main() {
    val templater = Templater()

    val template = templater.buildString { 
        title("Welcome!")
        inject("Dear user,") 
        inject(getWelcomeText())
    }

    print(template)
} 

Here templating logic gets extracted into a separate Templater class. It offers a clean DSL via the custom TemplateBuilder.

You can build rich text manipulation APIs on top of string formatting capabilities in this way.

Comparison with Other Languages

It helps to contextualize Kotlin‘s approach to string manipulation with some popular alternatives:

  • Java: More verbose with + concatenations. No multi-line support. Reliance on StringBuilder. Lacks good text templating option.
  • Python: Powerful f-string interpolation including expressions. Formatting and padding also available. But building strings incrementally needs separate io buffer.
  • JavaScript: Template literals provide simple substitution and multiline access like Kotlin raw strings. Plus tagged templates allow library integrations. But weak compared to Kotlin‘s strong typing, regex support and kotlinx libraries.
  • C#: Rich substitution syntax covering inline expressions, formatted specifiers etc. Interpolation also usable with System.Console writes.

Overall Kotlin provides a comprehensive toolkit combining substitution, templates and utilities like StringBuilder, Regex etc. This offers a flexible platform for varied text manipulation needs.

Key Takeaways

We covered a lot of ground around string formatting in Kotlin!

Here are the vital takeaways:

  • Prefer string templates over interpolation for dynamic text generation
  • Use format specifiers like %d and %.2f for fine-grained control
  • Extract constants and utilize StringBuilder for optimization
  • Build custom interpolators for safety and reusability
  • Integrate formatting with JSON, HTML libraries
  • Enable advanced regex search-replace operations
  • Construct complete templating engines using formatter foundations

This demonstrates that string handling is a Kotlin strong suit thanks to the multi-paradigm facilities.

Adopting these best practices will undoubtedly take your skills to an expert level.

So leverage Kotlin‘s formatting powers to craft exceptional string manipulation solutions!

Similar Posts