BAB 33: String Formatting

Kuasai fmt.Sprintf dan verb formatting Go untuk mengontrol tepat bagaimana data angka, string, struct, dan tipe lainnya ditampilkan sebagai teks.

Sepanjang ebook ini, kamu sudah mencetak berbagai jenis data — latensi jaringan dalam milidetik, pesan error yang informatif, laporan dengan nama dan angka. Semua itu menggunakan fmt.Printf dan fmt.Println secara langsung, seringkali dengan format yang cukup sederhana. Go menyediakan sistem formatting yang jauh lebih presisi dari itu: kamu bisa mengontrol berapa angka desimal yang muncul, bagaimana struct ditampilkan, apakah angka dicetak dalam basis desimal atau heksadesimal, dan banyak lagi. Semua ini dikontrol lewat verb formatting — notasi khusus yang dimulai dengan tanda %.

Fungsi Formatting di Package fmt

Package fmt menyediakan beberapa fungsi formatting. Perbedaannya ada di ke mana output dikirim dan apakah string hasil dibentuk atau langsung dicetak:

  • fmt.Printf(format, ...) — mencetak langsung ke stdout
  • fmt.Sprintf(format, ...) — mengembalikan string hasil formatting (tidak mencetak)
  • fmt.Fprintf(w, format, ...) — menulis ke io.Writer (file, HTTP response, dll)
  • fmt.Errorf(format, ...) — membuat nilai error dengan pesan terformat (sudah kamu pakai di Bab 32)

fmt.Sprintf adalah yang paling sering dibutuhkan saat kamu perlu menyimpan string terformat untuk diproses lebih lanjut, bukan langsung mencetak.

// format.go
package main

import "fmt"

func main() {
    suhu := 36.6
    nama := "sensor-A"

    // Sprintf menyimpan ke variabel
    pesan := fmt.Sprintf("pembacaan dari %s: %.1f derajat Celsius", nama, suhu)
    fmt.Println(pesan)

    // Printf langsung ke stdout
    fmt.Printf("status: %s — suhu %.1f°C\n", nama, suhu)
}
pembacaan dari sensor-A: 36.6 derajat Celsius
status: sensor-A — suhu 36.6°C

Verb untuk Angka

Verb formatting memberikan kontrol penuh atas representasi angka. Satu nilai yang sama bisa ditampilkan dalam berbagai bentuk tergantung verb yang digunakan.

// format.go
package main

import "fmt"

func main() {
    port := 8080
    rasio := 0.857142857

    fmt.Printf("desimal    : %d\n", port)
    fmt.Printf("biner      : %b\n", port)
    fmt.Printf("oktal      : %o\n", port)
    fmt.Printf("heksadesimal: %x\n", port)
    fmt.Printf("heksadesimal: %X\n", port) // huruf besar

    fmt.Printf("\n")
    fmt.Printf("float default : %f\n", rasio)
    fmt.Printf("2 desimal     : %.2f\n", rasio)
    fmt.Printf("saintifik     : %e\n", rasio)
    fmt.Printf("adaptif       : %g\n", rasio)
}
desimal    : 8080
biner      : 1111110010000
oktal      : 17620
heksadesimal: 1f90
heksadesimal: 1F90

float default : 0.857143
2 desimal     : 0.86
saintifik     : 8.571429e-01
adaptif       : 0.857142857

Verb %f default menampilkan 6 angka desimal. Notasi %.2f membatasi menjadi 2 desimal. %g memilih representasi paling ringkas — tanpa trailing zero yang tidak perlu.

Berikut referensi verb angka yang sering dipakai:

VerbDeskripsiContoh output (26)
%dDesimal (basis 10)26
%bBiner (basis 2)11010
%oOktal (basis 8)32
%xHeksadesimal huruf kecil1a
%XHeksadesimal huruf besar1A
%fFloat, 6 desimal default26.000000
%.NfFloat, N desimal%.2f26.00
%eNotasi saintifik2.600000e+01
%gFloat adaptif26

Verb untuk String dan Boolean

// format.go
package main

import "fmt"

func main() {
    status := true
    kode := "TXN-2024"
    karakter := 'A'

    fmt.Printf("string  : %s\n", kode)
    fmt.Printf("quoted  : %q\n", kode)   // dengan tanda kutip
    fmt.Printf("boolean : %t\n", status)
    fmt.Printf("karakter: %c\n", karakter) // dari nilai unicode
    fmt.Printf("unicode : %d\n", karakter) // nilai numeriknya
}
string  : TXN-2024
quoted  : "TXN-2024"
boolean : true
karakter: A
unicode : 65

Verb %q berguna saat kamu ingin menampilkan string beserta tanda kutipnya — misalnya saat mencetak nilai yang mungkin kosong atau mengandung spasi, agar pembaca log bisa melihatnya dengan jelas.

Verb untuk Struct dan Tipe

Verb %v, %+v, dan %#v adalah tiga level detail berbeda untuk menampilkan nilai struct — atau nilai tipe apapun.

// format.go
package main

import "fmt"

type Sensor struct {
    ID       string
    Lokasi   string
    Aktif    bool
    Pembacaan float64
}

func main() {
    s := Sensor{
        ID:        "SNS-007",
        Lokasi:    "Ruang Server",
        Aktif:     true,
        Pembacaan: 72.4,
    }

    fmt.Printf("%%v  : %v\n", s)
    fmt.Printf("%%+v : %+v\n", s)
    fmt.Printf("%%#v : %#v\n", s)
    fmt.Printf("%%T  : %T\n", s)  // nama tipe data
}
%v  : {SNS-007 Ruang Server true 72.4}
%+v : {ID:SNS-007 Lokasi:Ruang Server Aktif:true Pembacaan:72.4}
%#v : main.Sensor{ID:"SNS-007", Lokasi:"Ruang Server", Aktif:true, Pembacaan:72.4}
%T  : main.Sensor

%v menampilkan nilai saja — berguna untuk log ringkas. %+v menambahkan nama field — lebih mudah dibaca saat debug. %#v menampilkan representasi lengkap yang sintaksnya bisa langsung di-paste ke kode Go — ideal untuk memahami struktur nilai secara akurat.

%T sangat berguna saat bekerja dengan interface atau fungsi yang menerima any — ia menampilkan tipe konkret dari nilai tersebut, membantu debug tanpa perlu membuka definisi type assertion.

Lebar dan Padding

Selain presisi desimal, kamu bisa mengontrol lebar minimum output — berguna untuk membuat output yang terlihat rapi seperti tabel.

// format.go
package main

import "fmt"

func main() {
    data := []struct {
        nama  string
        nilai int
        rasio float64
    }{
        {"sensor-A", 1240, 0.82},
        {"sensor-B", 87, 0.9175},
        {"sensor-panjang", 5300, 0.6},
    }

    fmt.Printf("%-16s %6s %8s\n", "NAMA", "NILAI", "RASIO")
    fmt.Printf("%-16s %6s %8s\n", "────────────────", "──────", "────────")

    for _, d := range data {
        fmt.Printf("%-16s %6d %8.2f\n", d.nama, d.nilai, d.rasio)
    }
}
NAMA             NILAI    RASIO
──────────────── ────── ────────
sensor-A          1240     0.82
sensor-B            87     0.92
sensor-panjang    5300     0.60

Angka setelah % menentukan lebar minimum. Tanda minus (-) berarti rata kiri; tanpa minus berarti rata kanan. %6d menjamin kolom angka selebar minimal 6 karakter, dan %-16s menjamin kolom nama selebar minimal 16 karakter rata kiri.

Verb %% menghasilkan karakter % literal. Jika kamu perlu mencetak tanda persen dalam string format, gunakan %% — bukan %.

Latihan

Latihan 1 — Format laporan: Buat slice of struct HasilPemeriksaan dengan field KodeAlat string, Suhu float64, Tekanan float64, dan Status bool. Isi dengan 5 data. Cetak sebagai tabel dengan header, menggunakan padding dan presisi desimal yang konsisten.

Latihan 2 — Memformat pesan error: Modifikasi fungsi parsePeriode dari Bab 32 agar pesan errornya menggunakan fmt.Errorf dengan format yang menyertakan nilai input, rentang yang valid, dan nama fungsi yang memanggil (%s: nilai %d tidak valid, harus antara %d dan %d). Pastikan pesan yang dihasilkan informatif dan konsisten.

Latihan 3 — Debug struct: Buat fungsi inspeksi(v any) yang mencetak nilai menggunakan ketiga verb struct (%v, %+v, %#v) sekaligus dengan label. Panggil fungsi ini dengan struct Sensor dari contoh di atas dan dengan nilai primitif seperti integer, boolean, dan string — amati bagaimana output berbeda untuk setiap tipe.

Formatting string mungkin terasa seperti detail kecil, tapi ia ada di setiap titik di mana program berkomunikasi dengan dunia luar — log, pesan error, output terminal, template. Dengan verb formatting yang sudah kamu kuasai, output program Go yang kamu tulis akan jauh lebih informatif dan mudah dibaca. Sejauh ini semua data yang kita tampilkan berasal dari nilai yang sudah ditentukan di kode — tapi bagaimana kalau programnya perlu menghasilkan data yang tidak bisa diprediksi, seperti urutan acak atau angka untuk simulasi? Di bab berikutnya, kita akan menjelajahi math/rand dan cara kerja pseudo-random number generation di Go.

Referensi

  1. 1Package fmt — Go Standard Library Documentation
  2. 2String Formatting — Go by Example