Raw string """...""" memecahkan masalah penulisan teks multi-baris, tetapi membawa masalah baru: indentasi dari kode sumber ikut tersimpan dalam string. Tanpa perawatan khusus, blok JSON atau SQL yang ditulis menjorok di dalam function akan berisi whitespace yang tidak diinginkan pada hasil cetak. Kotlin menyediakan dua method untuk merapikan situasi ini: trimIndent() yang menghapus indentasi umum secara otomatis, dan trimMargin(prefix) yang menggunakan penanda eksplisit. Pada chapter ini kita akan membedah keduanya beserta pola penggunaan yang umum di proyek nyata.

Masalah: Indentasi Ikut Tersimpan #

Saat kita menulis raw string di dalam function, kode sumber biasanya menjorok beberapa level. Indentasi tersebut ikut tersimpan dalam string:

kotlin Contoh: masalah indentasi
fun cetak() {
    val teks = """
        Baris pertama
        Baris kedua
    """
    println(teks)
}

Output di atas akan berisi baris kosong pertama, lalu delapan spasi sebelum Baris pertama, dan empat spasi sebelum """ penutup. Untuk mendapatkan output yang rapi, kita perlu membersihkan indentasi.

trimIndent — Hapus Indentasi Umum #

Method trimIndent() melakukan tiga langkah: (1) membuang baris kosong di awal dan akhir, (2) mendeteksi indentasi minimum di seluruh baris non-kosong, (3) memotong indentasi sebesar itu dari setiap baris. Hasilnya adalah string yang setiap barisnya dimulai dari kolom 0 relatif terhadap indentasi paling sedikit di sumber.

kotlin Contoh: trimIndent
val teks = """
    Baris pertama
    Baris kedua
    Baris ketiga
""".trimIndent()
println(teks)

Output:

text
Baris pertama
Baris kedua
Baris ketiga

Aturan indentasi minimum penting ketika beberapa baris memiliki indentasi yang berbeda. Misalnya, jika satu baris menjorok empat spasi dan baris lain menjorok delapan spasi, trimIndent akan memotong empat spasi dari semua baris — sehingga baris yang menjorok delapan tetap memiliki empat spasi tambahan sebagai indentasi internal.

trimMargin — Penanda Eksplisit #

Method trimMargin() menghapus semua karakter whitespace sebelum tanda margin (default |) dan tanda margin itu sendiri, untuk setiap baris. Pendekatan ini memberi kontrol eksplisit: hanya karakter setelah | yang dipertahankan. Hal ini berguna ketika baris memiliki indentasi internal yang sengaja dibuat tidak rata.

kotlin Contoh: trimMargin default
val teks = """
    |Baris pertama
    |Baris kedua
    |Baris ketiga
""".trimMargin()
println(teks)

Karakter margin default adalah |, tetapi bisa diganti dengan parameter: trimMargin("#"), trimMargin(">"), dan seterusnya. Pemilihan karakter margin penting bila konten string mengandung | sebagai karakter data (misalnya pada SQL dengan operator ||).

Pola Umum: SQL dan Surat #

Dua pola yang paling sering ditemui adalah query SQL dan template surat. Untuk SQL, trimIndent sudah cukup karena tidak ada indentasi internal yang perlu dipertahankan:

kotlin Pola: query SQL
val sql = """
    SELECT id, nama
    FROM pengguna
    WHERE aktif = true
""".trimIndent()

Untuk surat dengan indentasi paragraf, trimMargin lebih cocok karena kita bisa menjorokkan baris tertentu secara sengaja:

kotlin Pola: surat dengan indentasi
val surat = """
    |Halo pelanggan,
    |Terima kasih telah berlangganan.
    |  (catatan: mohon periksa email kedua)
    |Salam hangat,
""".trimMargin()

Baris (catatan...) memiliki dua spasi sebelum | yang akan dihapus, tetapi dua spasi setelah | tetap menjadi indentasi internal yang sengaja dibuat.

Contoh Lengkap dan Outputnya #

File trim.kt berikut membandingkan keempat pendekatan secara berurutan:

kotlin
fun main() {
    val tanpaTrim = """
        Baris pertama
        Baris kedua
        Baris ketiga
    """
    println("Tanpa trimIndent:")
    println("[${tanpaTrim}]")
    println("---")

    val denganTrimIndent = """
        Baris pertama
        Baris kedua
        Baris ketiga
    """.trimIndent()
    println("Dengan trimIndent:")
    println("[${denganTrimIndent}]")
    println("---")

    val denganMargin = """
        |Baris pertama
        |Baris kedua
        |Baris ketiga
    """.trimMargin()
    println("Dengan trimMargin (default |):")
    println("[${denganMargin}]")
    println("---")

    val denganPrefixKhusus = """
        #Baris pertama
        #Baris kedua
        #Baris ketiga
    """.trimMargin("#")
    println("Dengan trimMargin('#'):")
    println("[${denganPrefixKhusus}]")
    println("---")

    val indentCampuran = """
        |Pertama
        |  Kedua menjorok
        |Ketiga
    """.trimMargin()
    println("Dengan indent internal:")
    println("[${indentCampuran}]")
    println("---")

    val sql = """
        |SELECT id, nama
        |FROM pengguna
        |WHERE aktif = true
    """.trimMargin()
    println("Query dengan trimMargin:")
    println(sql)
    println("---")

    val template = """
        |Halo ${'$'}{nama},
        |Selamat datang di layanan kami.
    """.trimMargin()
    println("Template surat:")
    println(template)
}

Output yang Dihasilkan:

text
Tanpa trimIndent:
[
        Baris pertama
        Baris kedua
        Baris ketiga
    ]
---
Dengan trimIndent:
[Baris pertama
Baris kedua
Baris ketiga]
---
Dengan trimMargin (default |):
[Baris pertama
Baris kedua
Baris ketiga]
---
Dengan trimMargin('#'):
[Baris pertama
Baris kedua
Baris ketiga]
---
Dengan indent internal:
[Pertama
  Kedua menjorok
Ketiga]
---
Query dengan trimMargin:
SELECT id, nama
FROM pengguna
WHERE aktif = true
---
Template surat:
Halo ${nama},
Selamat datang di layanan kami.

Mari kita telaah bagian penting dari kode di atas. Blok pertama tanpaTrim membungkus string dalam tanda [...] agar terlihat baris kosong dan indentasi yang ikut tersimpan — perhatikan baris kosong pertama dan spasi sebelum Baris pertama. Blok denganTrimIndent menghapus baris kosong di awal/akhir serta indentasi minimum delapan spasi, menghasilkan tiga baris rapi tanpa spasi awal. Blok denganMargin memakai prefix | sebagai penanda; semua karakter sebelum | (termasuk spasi) dihapus. Blok denganPrefixKhusus menunjukkan parameter kustom # berguna ketika konten mengandung |. Blok indentCampuran memperlihatkan indentasi internal yang sengaja dibuat: Kedua menjorok tetap memiliki dua spasi setelah trimMargin. Terakhir, blok template menggunakan ${'$'}{nama} untuk menyisipkan teks literal ${nama} (bukan template) — sebuah trik untuk menulis template string yang akan diproses di tahap berikutnya.

Perbandingan trimIndent vs trimMargin #

AspektrimIndent()trimMargin(prefix)
StrategiIndentasi minimum otomatisPenanda eksplisit
Karakter penandaTidak ada`
Indentasi internalDipertahankan relatif terhadap minimumHarus ditulis setelah penanda
Cocok untukTeks rapi satu levelTeks dengan indentasi beragam
RisikoSalah bila ada baris tak sengaja menjorokLupa menulis `

Referensi Lanjutan #

  • Strings — bagian trimIndent() dan trimMargin() pada panduan strings

Penutup #

Dengan trimIndent dan trimMargin, kita kini punya alat lengkap untuk menyusun teks multi-baris yang rapi secara visual di sumber sekaligus bersih di output. Selanjutnya, kita akan masuk ke topik pencocokan pola yang lebih kompleks: regular expression — fitur yang memungkinkan kita mencari, memvalidasi, dan mengganti teks berdasarkan pola, bukan literal.