BAB 38: Package strings
Pelajari cara memanipulasi string di Go menggunakan package strings: pencarian, penggantian, pemisahan, penggabungan, dan transformasi teks.
Di bab sebelumnya, kamu melihat bagaimana strconv menjembatani string dan tipe numerik. Tapi begitu kamu punya string, sering kali kamu perlu bekerja di dalam string itu sendiri: apakah teks ini mengandung kata tertentu? Bagaimana cara memotong string berdasarkan pemisah? Bagaimana mengubah semua huruf menjadi huruf kecil untuk perbandingan yang konsisten?
Semua itu ditangani oleh package strings dari standard library Go. Package ini berisi lebih dari 20 fungsi untuk pencarian, penggantian, pemisahan, penggabungan, dan transformasi string — tanpa perlu dependensi eksternal sama sekali.
Pencarian di Dalam String
Kebutuhan paling dasar saat bekerja dengan string adalah mencari apakah suatu teks ada di dalamnya, di mana posisinya, dan berapa kali ia muncul.
// teks.go
package main
import (
"fmt"
"strings"
)
func main() {
judul := "Laporan Keuangan Kuartal Tiga 2025"
// apakah substring ada?
fmt.Println(strings.Contains(judul, "Keuangan")) // true
fmt.Println(strings.Contains(judul, "keuangan")) // false — case-sensitive
// apakah diawali atau diakhiri dengan teks tertentu?
fmt.Println(strings.HasPrefix(judul, "Laporan")) // true
fmt.Println(strings.HasSuffix(judul, "2025")) // true
// posisi pertama substring (-1 jika tidak ditemukan)
fmt.Println(strings.Index(judul, "Kuartal")) // 20
fmt.Println(strings.Index(judul, "Tahunan")) // -1
// berapa kali muncul?
kalimat := "buku itu bagus, buku itu mahal, buku itu langka"
fmt.Println(strings.Count(kalimat, "buku")) // 3
}
true
false
true
true
20
-1
3
strings.Contains adalah yang paling sering dipakai untuk validasi sederhana. strings.Index berguna ketika kamu perlu tahu posisi persisnya — misalnya untuk memotong string secara manual. Keduanya bekerja berdasarkan perbandingan byte, bukan karakter, jadi case-sensitive secara default.
Untuk pencarian yang tidak peduli huruf besar/kecil, ubah kedua string ke lowercase terlebih dahulu sebelum membandingkan: strings.Contains(strings.ToLower(teks), strings.ToLower(kata)).
Penggantian Teks
strings.Replace mengganti kemunculan substring dengan teks baru. Parameter keempat mengontrol berapa kemunculan yang diganti — gunakan -1 untuk mengganti semua.
// teks.go
package main
import (
"fmt"
"strings"
)
func main() {
template := "Halo {nama}, pesanan #{id} sudah {nama} konfirmasi."
// ganti semua kemunculan
hasil := strings.Replace(template, "{nama}", "Budi", -1)
fmt.Println(hasil)
// ganti hanya kemunculan pertama
hasilSatu := strings.Replace(template, "{nama}", "Budi", 1)
fmt.Println(hasilSatu)
// ReplaceAll — shorthand untuk Replace dengan n=-1
bersih := strings.ReplaceAll(" banyak spasi di sini ", " ", " ")
fmt.Println(bersih)
}
Halo Budi, pesanan #{id} sudah Budi konfirmasi.
Halo Budi, pesanan #{id} sudah {nama} konfirmasi.
banyak spasi di sini
strings.ReplaceAll adalah alias yang lebih eksplisit untuk strings.Replace dengan n = -1. Gunakan mana yang lebih jelas sesuai konteks.
Pemisahan dan Penggabungan
strings.Split memotong string menjadi slice berdasarkan separator. Kebalikannya, strings.Join, menggabungkan slice menjadi satu string dengan separator di antara elemen.
// teks.go
package main
import (
"fmt"
"strings"
)
func main() {
// memisahkan CSV sederhana
baris := "Jakarta,Surabaya,Bandung,Medan,Semarang"
kota := strings.Split(baris, ",")
fmt.Printf("jumlah kota: %d\n", len(kota))
fmt.Printf("kota pertama: %s\n", kota[0])
fmt.Printf("kota terakhir: %s\n", kota[len(kota)-1])
// menggabungkan kembali dengan separator berbeda
daftarRapi := strings.Join(kota, " | ")
fmt.Println(daftarRapi)
// memisahkan per baris
paragraf := "baris pertama\nbaris kedua\nbaris ketiga"
baris2 := strings.Split(paragraf, "\n")
for i, b := range baris2 {
fmt.Printf("%d: %s\n", i+1, b)
}
}
jumlah kota: 5
kota pertama: Jakarta
kota terakhir: Semarang
Jakarta | Surabaya | Bandung | Medan | Semarang
1: baris pertama
2: baris kedua
3: baris ketiga
Pola Split lalu Join sangat berguna untuk normalisasi data — misalnya memparse input pengguna yang mungkin menggunakan pemisah tidak konsisten.
Transformasi Teks
Tiga fungsi transformasi yang paling sering dipakai: mengubah ke huruf kecil, huruf besar, dan menghapus spasi di tepi.
// teks.go
package main
import (
"fmt"
"strings"
)
func main() {
masukan := " NAMA PENGGUNA: gopher_123 "
// normalisasi: trim spasi, lowercase
bersih := strings.TrimSpace(masukan)
fmt.Printf("setelah trim: %q\n", bersih)
lower := strings.ToLower(bersih)
fmt.Printf("lowercase: %q\n", lower)
upper := strings.ToUpper("peringatan: disk hampir penuh")
fmt.Printf("uppercase: %q\n", upper)
// Trim karakter tertentu (bukan hanya spasi)
versi := "---v1.4.2---"
fmt.Printf("trim dash: %q\n", strings.Trim(versi, "-"))
fmt.Printf("trim prefix: %q\n", strings.TrimPrefix(versi, "---"))
fmt.Printf("trim suffix: %q\n", strings.TrimSuffix(versi, "---"))
}
setelah trim: "NAMA PENGGUNA: gopher_123"
lowercase: "nama pengguna: gopher_123"
uppercase: "PERINGATAN: DISK HAMPIR PENUH"
trim dash: "v1.4.2"
trim prefix: "v1.4.2---"
trim suffix: "---v1.4.2"
strings.TrimSpace menghapus semua whitespace (spasi, tab, newline) di awal dan akhir string. strings.Trim lebih fleksibel — ia menghapus karakter apapun yang ada dalam string karakter yang kamu tentukan. TrimPrefix dan TrimSuffix lebih presisi: hanya menghapus jika string benar-benar diawali atau diakhiri dengan prefix/suffix yang ditentukan.
Pengulangan dan Perakitan
strings.Repeat mengulang string sebanyak n kali. Ini berguna untuk menghasilkan padding, separator, atau template yang repetitif.
// teks.go
package main
import (
"fmt"
"strings"
)
func cetakHeader(judul string) {
lebar := len(judul) + 4
garis := strings.Repeat("=", lebar)
fmt.Println(garis)
fmt.Printf(" %s \n", judul)
fmt.Println(garis)
}
func main() {
cetakHeader("Laporan Penjualan")
cetakHeader("Ringkasan")
// padding angka dengan nol di depan
for i := 1; i <= 5; i++ {
nomor := fmt.Sprintf("%d", i)
pad := strings.Repeat("0", 3-len(nomor)) + nomor
fmt.Printf("INV-%s\n", pad)
}
}
====================
Laporan Penjualan
====================
===========
Ringkasan
===========
INV-001
INV-002
INV-003
INV-004
INV-005
Studi Kasus: Parser Log Sederhana
Menggabungkan beberapa fungsi strings dalam satu program yang nyata memperlihatkan betapa powerful-nya package ini untuk pemrosesan teks.
// teks.go
package main
import (
"fmt"
"strings"
)
type EntriLog struct {
Level string
Sumber string
Pesan string
}
func parseLog(baris string) (EntriLog, bool) {
// format: "[LEVEL] sumber: pesan"
baris = strings.TrimSpace(baris)
if !strings.HasPrefix(baris, "[") {
return EntriLog{}, false
}
// ambil level: teks antara [ dan ]
tutup := strings.Index(baris, "]")
if tutup == -1 {
return EntriLog{}, false
}
level := baris[1:tutup]
// sisa setelah "] "
sisa := strings.TrimSpace(baris[tutup+1:])
// pisahkan sumber dan pesan berdasarkan ":"
bagian := strings.SplitN(sisa, ":", 2)
if len(bagian) != 2 {
return EntriLog{}, false
}
return EntriLog{
Level: strings.ToUpper(level),
Sumber: strings.TrimSpace(bagian[0]),
Pesan: strings.TrimSpace(bagian[1]),
}, true
}
func main() {
logLines := []string{
"[INFO] auth: pengguna berhasil masuk",
"[ERROR] database: koneksi timeout setelah 30 detik",
"[WARN] cache: kapasitas mencapai 80%",
"baris tidak valid",
"[DEBUG] router: menerima request GET /api/laporan",
}
for _, baris := range logLines {
entri, ok := parseLog(baris)
if !ok {
fmt.Printf("dilewati: %q\n", baris)
continue
}
fmt.Printf("[%-5s] %-10s — %s\n", entri.Level, entri.Sumber, entri.Pesan)
}
}
[INFO ] auth — pengguna berhasil masuk
[ERROR] database — koneksi timeout setelah 30 detik
[WARN ] cache — kapasitas mencapai 80%
dilewati: "baris tidak valid"
[DEBUG] router — menerima request GET /api/laporan
strings.SplitN adalah varian dari Split yang membatasi jumlah potongan — SplitN(s, ":", 2) memotong maksimal dua bagian, sehingga tanda titik dua di dalam pesan tidak ikut dipotong.
Semua fungsi di package strings bekerja pada level byte, bukan karakter Unicode. Untuk string yang mengandung karakter multibyte (seperti karakter CJK atau emoji), gunakan package unicode/utf8 atau iterasi dengan for range yang bekerja per rune.
Latihan
Latihan 1 — Normalisasi input:
Tulis fungsi normalisasiTag(input string) []string yang menerima string berisi tag dipisahkan koma (contoh: " Go , backend , API , go ") dan mengembalikan slice tag yang sudah di-trim, lowercase, dan bebas duplikat.
Latihan 2 — Template sederhana:
Buat fungsi isiTemplate(template string, data map[string]string) string yang mengganti semua placeholder berbentuk {key} dengan nilai yang sesuai dari map. Jika key tidak ada di map, biarkan placeholder apa adanya.
Latihan 3 — Penghitung kata:
Buat fungsi hitungKata(teks string) map[string]int yang mengembalikan peta frekuensi kemunculan setiap kata dalam teks. Abaikan perbedaan huruf besar/kecil, dan pisahkan kata berdasarkan spasi. Uji dengan kalimat: "go adalah bahasa yang bagus go mudah dipelajari bahasa go".
Package strings dan strconv bersama-sama membentuk fondasi pemrosesan teks di Go. Kamu kini bisa mengurai, memvalidasi, memformat, dan mentransformasi data berbasis teks dengan percaya diri. Namun semua fungsi di strings bekerja dengan pola yang tetap — kamu harus tahu persis string apa yang dicari. Bagaimana jika kamu perlu mencocokkan sesuatu yang lebih abstrak, seperti “teks yang terlihat seperti tanggal” atau “semua kata yang diawali huruf kapital”? Di situlah regular expression masuk — dan itu yang akan kita jelajahi di bab selanjutnya.