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 stdoutfmt.Sprintf(format, ...)— mengembalikan string hasil formatting (tidak mencetak)fmt.Fprintf(w, format, ...)— menulis keio.Writer(file, HTTP response, dll)fmt.Errorf(format, ...)— membuat nilaierrordengan 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:
| Verb | Deskripsi | Contoh output (26) |
|---|---|---|
%d | Desimal (basis 10) | 26 |
%b | Biner (basis 2) | 11010 |
%o | Oktal (basis 8) | 32 |
%x | Heksadesimal huruf kecil | 1a |
%X | Heksadesimal huruf besar | 1A |
%f | Float, 6 desimal default | 26.000000 |
%.Nf | Float, N desimal | %.2f → 26.00 |
%e | Notasi saintifik | 2.600000e+01 |
%g | Float adaptif | 26 |
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.