Strconv #
Setiap aplikasi Go yang membaca input dari luar — form web, argumen CLI, file konfigurasi, query parameter URL, atau pesan antrian — pasti perlu mengonversi string menjadi tipe data yang bisa diolah: integer untuk ID dan angka, float untuk nilai desimal, bool untuk flag aktif/nonaktif. Package strconv adalah alat untuk semua konversi ini. Ia lebih cepat dan lebih eksplisit dari fmt.Sprintf karena dirancang khusus untuk satu tujuan: mengonversi antara string dan tipe primitif Go. Memahami strconv dengan baik berarti memahami cara menangani input yang tidak bisa dipercaya dengan benar — karena setiap konversi yang bisa gagal mengembalikan error yang harus diperiksa, bukan diabaikan.
Gambaran Besar Package strconv #
flowchart LR
subgraph Parse["String → Tipe Lain"]
P1["strconv.Atoi\nstring → int"]
P2["strconv.ParseInt\nstring → int64 (basis apapun)"]
P3["strconv.ParseFloat\nstring → float64"]
P4["strconv.ParseBool\nstring → bool"]
P5["strconv.ParseUint\nstring → uint64"]
end
subgraph Format["Tipe Lain → String"]
F1["strconv.Itoa\nint → string"]
F2["strconv.FormatInt\nint64 → string (basis apapun)"]
F3["strconv.FormatFloat\nfloat64 → string"]
F4["strconv.FormatBool\nbool → string"]
F5["strconv.FormatUint\nuint64 → string"]
end
subgraph Quote["String Escaping"]
Q1["strconv.Quote\ntambahkan tanda kutip & escape"]
Q2["strconv.Unquote\nhapus tanda kutip & unescape"]
Q3["strconv.AppendQuote\nappend ke []byte"]
end
subgraph Errors["Error yang Mungkin"]
E1["*strconv.NumError\n .Err: ErrSyntax\n .Err: ErrRange\n .Num: string input"]
end
Parse --> Errors
Format --> Str["string"]
Parse --> Val["nilai Go"]
style Parse fill:#e8f5e9
style Format fill:#e3f2fd
style Quote fill:#fff3e0
style Errors fill:#fce4ecKonversi Integer #
Atoi dan Itoa — Shortcut Paling Umum #
Atoi (ASCII to Integer) dan Itoa (Integer to ASCII) adalah fungsi yang paling sering dipakai dari package strconv — konversi langsung antara string dan int.
package main
import (
"fmt"
"strconv"
)
func main() {
// Itoa — int ke string, tidak pernah gagal
s := strconv.Itoa(42)
fmt.Println(s) // "42"
fmt.Printf("%T\n", s) // string
s2 := strconv.Itoa(-100)
fmt.Println(s2) // "-100"
// Atoi — string ke int, bisa gagal
n, err := strconv.Atoi("42")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(n) // 42
fmt.Printf("%T\n", n) // int
// Error saat input bukan angka
_, err = strconv.Atoi("abc")
fmt.Println(err) // strconv.Atoi: parsing "abc": invalid syntax
// Error saat angka terlalu besar untuk int
_, err = strconv.Atoi("99999999999999999999999")
fmt.Println(err) // strconv.Atoi: parsing "99999999999999999999999": value out of range
}
ParseInt — Kontrol Penuh #
ParseInt memberikan kontrol atas basis bilangan (desimal, heksadesimal, oktal, biner) dan ukuran bit hasil:
// ParseInt(s string, base int, bitSize int) (int64, error)
// base: 0 (deteksi otomatis), 2, 8, 10, 16
// bitSize: 0 (int), 8, 16, 32, 64
// Basis 10 — desimal biasa
n, _ := strconv.ParseInt("255", 10, 64)
fmt.Println(n) // 255
// Basis 16 — heksadesimal
n, _ = strconv.ParseInt("ff", 16, 64)
fmt.Println(n) // 255
n, _ = strconv.ParseInt("FF", 16, 64)
fmt.Println(n) // 255
// Basis 2 — biner
n, _ = strconv.ParseInt("11111111", 2, 64)
fmt.Println(n) // 255
// Basis 8 — oktal
n, _ = strconv.ParseInt("377", 8, 64)
fmt.Println(n) // 255
// Basis 0 — deteksi otomatis dari prefix
n, _ = strconv.ParseInt("0xff", 0, 64) // 0x prefix → hex
fmt.Println(n) // 255
n, _ = strconv.ParseInt("0377", 0, 64) // 0 prefix → oktal
fmt.Println(n) // 255
n, _ = strconv.ParseInt("0b11111111", 0, 64) // 0b prefix → biner
fmt.Println(n) // 255
n, _ = strconv.ParseInt("255", 0, 64) // tanpa prefix → desimal
fmt.Println(n) // 255
// bitSize membatasi rentang yang valid
n32, err := strconv.ParseInt("32768", 10, 16) // max int16 adalah 32767
fmt.Println(n32, err)
// 32767 strconv.ParseInt: parsing "32768": value out of range
// Perhatikan: nilai yang dikembalikan adalah batas atas (clamped), bukan 0!
FormatInt — Integer ke String dengan Basis #
// FormatInt(i int64, base int) string
n := int64(255)
fmt.Println(strconv.FormatInt(n, 10)) // "255" — desimal
fmt.Println(strconv.FormatInt(n, 16)) // "ff" — heksadesimal huruf kecil
fmt.Println(strconv.FormatInt(n, 2)) // "11111111" — biner
fmt.Println(strconv.FormatInt(n, 8)) // "377" — oktal
fmt.Println(strconv.FormatInt(n, 36)) // "73" — basis 36 (0-9, a-z)
// Untuk int biasa (bukan int64), konversi dulu
x := 42
fmt.Println(strconv.FormatInt(int64(x), 16)) // "2a"
// Atau gunakan Itoa untuk basis 10
fmt.Println(strconv.Itoa(x)) // "42"
ParseUint dan FormatUint — Unsigned Integer #
// Untuk nilai yang tidak pernah negatif (ID, ukuran, port)
u, err := strconv.ParseUint("65535", 10, 16) // uint16 max
fmt.Println(u, err) // 65535 <nil>
// Port number — uint16
port, err := strconv.ParseUint("8080", 10, 16)
if err != nil {
fmt.Println("port tidak valid:", err)
return
}
fmt.Printf("Port: %d\n", port) // Port: 8080
// Format unsigned
fmt.Println(strconv.FormatUint(uint64(255), 16)) // "ff"
fmt.Println(strconv.FormatUint(uint64(255), 2)) // "11111111"
Konversi Float #
ParseFloat — String ke Float #
// ParseFloat(s string, bitSize int) (float64, error)
// bitSize: 32 untuk float32, 64 untuk float64
// Parsing float64
f, err := strconv.ParseFloat("3.14159", 64)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(f) // 3.14159
fmt.Printf("%T\n", f) // float64
// Notasi ilmiah
f, _ = strconv.ParseFloat("1.5e10", 64)
fmt.Println(f) // 1.5e+10
f, _ = strconv.ParseFloat("2.5E-3", 64)
fmt.Println(f) // 0.0025
// Nilai khusus
f, _ = strconv.ParseFloat("Inf", 64)
fmt.Println(f) // +Inf
f, _ = strconv.ParseFloat("-Inf", 64)
fmt.Println(f) // -Inf
f, _ = strconv.ParseFloat("NaN", 64)
fmt.Println(f) // NaN
// bitSize 32 — presisi float32 tapi dikembalikan sebagai float64
f32, _ := strconv.ParseFloat("3.14159265358979", 32)
fmt.Println(f32) // 3.1415927410125732 — presisi float32
fmt.Println(float32(f32)) // 3.1415927 — cast ke float32
FormatFloat — Float ke String #
FormatFloat memberikan kontrol penuh atas format dan presisi output — ini yang membedakannya dari fmt.Sprintf("%.2f", f).
// FormatFloat(f float64, fmt byte, prec, bitSize int) string
// fmt: 'f' (desimal), 'e' (ilmiah), 'g' (terpendek), 'b' (biner), 'x' (hex)
// prec: presisi (-1 untuk presisi minimum yang mewakili nilai dengan tepat)
// bitSize: 32 atau 64
f := 3.14159265358979
// Format 'f' — desimal tetap
fmt.Println(strconv.FormatFloat(f, 'f', 2, 64)) // "3.14"
fmt.Println(strconv.FormatFloat(f, 'f', 5, 64)) // "3.14159"
fmt.Println(strconv.FormatFloat(f, 'f', -1, 64)) // "3.14159265358979"
// Format 'e' — notasi ilmiah
fmt.Println(strconv.FormatFloat(f, 'e', 2, 64)) // "3.14e+00"
fmt.Println(strconv.FormatFloat(f, 'e', -1, 64)) // "3.14159265358979e+00"
// Format 'g' — terpendek (ilmiah atau desimal, tergantung mana lebih pendek)
fmt.Println(strconv.FormatFloat(f, 'g', -1, 64)) // "3.14159265358979"
fmt.Println(strconv.FormatFloat(1e10, 'g', -1, 64)) // "1e+10"
// ANTI-PATTERN: Sprintf untuk round-trip float
pi := 3.14159265358979323846
s := fmt.Sprintf("%f", pi) // "3.141593" — kehilangan presisi!
f2, _ := strconv.ParseFloat(s, 64)
fmt.Println(f2 == pi) // false — tidak sama dengan aslinya
// BENAR: FormatFloat dengan prec -1 untuk round-trip yang tepat
s2 := strconv.FormatFloat(pi, 'f', -1, 64)
f3, _ := strconv.ParseFloat(s2, 64)
fmt.Println(f3 == pi) // true — nilai persis sama
Konversi Boolean #
// ParseBool — string ke bool
// Menerima: "1", "t", "T", "TRUE", "true", "True" → true
// Menerima: "0", "f", "F", "FALSE", "false", "False" → false
b, err := strconv.ParseBool("true")
fmt.Println(b, err) // true <nil>
b, _ = strconv.ParseBool("1")
fmt.Println(b) // true
b, _ = strconv.ParseBool("T")
fmt.Println(b) // true
b, _ = strconv.ParseBool("false")
fmt.Println(b) // false
b, _ = strconv.ParseBool("0")
fmt.Println(b) // false
_, err = strconv.ParseBool("yes") // tidak valid!
fmt.Println(err) // strconv.ParseBool: parsing "yes": invalid syntax
// FormatBool — bool ke string
fmt.Println(strconv.FormatBool(true)) // "true"
fmt.Println(strconv.FormatBool(false)) // "false"
Pola: Membaca Flag dari Environment #
// Environment variable sering direpresentasikan sebagai bool
func getEnvBool(key string, defaultVal bool) bool {
val, ada := os.LookupEnv(key)
if !ada || val == "" {
return defaultVal
}
b, err := strconv.ParseBool(val)
if err != nil {
// Log warning — nilai tidak valid, pakai default
fmt.Fprintf(os.Stderr, "warning: %s=%q bukan bool valid, pakai %v\n",
key, val, defaultVal)
return defaultVal
}
return b
}
// Penggunaan
debugMode := getEnvBool("APP_DEBUG", false)
tlsEnabled := getEnvBool("TLS_ENABLED", true)
Memahami NumError #
Semua fungsi Parse* mengembalikan *strconv.NumError saat gagal. Memahami strukturnya memungkinkan penanganan error yang lebih spesifik.
flowchart TD
Err["*strconv.NumError"] --> Func["Func: nama fungsi\n('Atoi', 'ParseInt', dll)"]
Err --> Num["Num: string input\nyang gagal diparse"]
Err --> ErrType["Err: jenis error"]
ErrType --> Syntax["strconv.ErrSyntax\ninput bukan format angka valid\ncontoh: 'abc', '12.3' untuk int"]
ErrType --> Range["strconv.ErrRange\nangka valid tapi di luar rentang\ncontoh: '999' untuk uint8 (max 255)"]
Syntax --> Handle1["Tampilkan pesan\n'format tidak valid'"]
Range --> Handle2["Tampilkan pesan\n'angka terlalu besar/kecil'"]
style Err fill:#fce4ec
style Syntax fill:#fff3e0
style Range fill:#ffebeeimport (
"errors"
"strconv"
)
func parseIDPengguna(s string) (int64, error) {
id, err := strconv.ParseInt(s, 10, 64)
if err != nil {
// Periksa jenis error untuk pesan yang lebih informatif
var numErr *strconv.NumError
if errors.As(err, &numErr) {
switch numErr.Err {
case strconv.ErrSyntax:
return 0, fmt.Errorf("ID pengguna %q bukan angka valid", s)
case strconv.ErrRange:
return 0, fmt.Errorf("ID pengguna %q terlalu besar", s)
}
}
return 0, fmt.Errorf("parseIDPengguna: %w", err)
}
if id <= 0 {
return 0, fmt.Errorf("ID pengguna harus positif, dapat: %d", id)
}
return id, nil
}
// Penggunaan
id, err := parseIDPengguna("abc")
// error: ID pengguna "abc" bukan angka valid
id, err = parseIDPengguna("99999999999999999999")
// error: ID pengguna "99999999999999999999" terlalu besar
id, err = parseIDPengguna("42")
// id: 42, err: nil
Quote dan Unquote — Escaping String #
Fungsi Quote dan Unquote berguna untuk debugging, logging, dan menangani string yang mungkin mengandung karakter spesial atau tidak bisa dicetak.
// Quote — tambahkan tanda kutip ganda dan escape karakter spesial
s := "Halo\tDunia\n"
fmt.Println(strconv.Quote(s))
// "Halo\tDunia\n" — tampil dengan escape sequence literal
s2 := `Ini "dikutip" dan ini\tbackslash`
fmt.Println(strconv.Quote(s2))
// "Ini \"dikutip\" dan ini\\tbackslash"
// Karakter Unicode
s3 := "Bahasa Indonesia: é à ü"
fmt.Println(strconv.Quote(s3))
// "Bahasa Indonesia: é à ü" — karakter printable tidak di-escape
s4 := string([]byte{0x00, 0x01, 0x1f}) // karakter kontrol
fmt.Println(strconv.Quote(s4))
// "\x00\x01\x1f"
// QuoteToASCII — escape semua non-ASCII
fmt.Println(strconv.QuoteToASCII("Héllo"))
// "H\u00e9llo"
// Unquote — kebalikan dari Quote
original, err := strconv.Unquote(`"Halo\tDunia\n"`)
fmt.Println(original, err)
// Halo Dunia
// <nil>
// IsPrint — apakah rune bisa dicetak?
fmt.Println(strconv.IsPrint('A')) // true
fmt.Println(strconv.IsPrint('\t')) // false — tab bukan printable
fmt.Println(strconv.IsPrint('é')) // true
// CanBackquote — apakah string bisa diwakili sebagai raw string literal?
fmt.Println(strconv.CanBackquote("Hello World")) // true
fmt.Println(strconv.CanBackquote("Hello\nWorld")) // false — ada newline
fmt.Println(strconv.CanBackquote("Hello`World")) // false — ada backtick
Append Variants — Zero Allocation #
Package strconv menyediakan varian Append* untuk semua fungsi Format — ini memungkinkan konversi langsung ke slice byte yang sudah ada tanpa alokasi string baru.
// Append variants — berguna untuk membangun output tanpa alokasi ekstra
buf := make([]byte, 0, 64)
// AppendInt — append representasi integer ke slice
buf = strconv.AppendInt(buf, 255, 16) // ff
buf = append(buf, ' ')
buf = strconv.AppendInt(buf, 255, 2) // 11111111
buf = append(buf, ' ')
buf = strconv.AppendInt(buf, -42, 10) // -42
fmt.Println(string(buf)) // "ff 11111111 -42"
// AppendFloat
buf = buf[:0] // reset tanpa realokasi
buf = strconv.AppendFloat(buf, 3.14159, 'f', 2, 64)
fmt.Println(string(buf)) // "3.14"
// AppendBool
buf = buf[:0]
buf = strconv.AppendBool(buf, true)
buf = append(buf, '/')
buf = strconv.AppendBool(buf, false)
fmt.Println(string(buf)) // "true/false"
// AppendQuote
buf = buf[:0]
buf = strconv.AppendQuote(buf, "Hello\tWorld")
fmt.Println(string(buf)) // "Hello\tWorld" (dengan tanda kutip)
Pola Append sangat berguna saat membangun response HTTP, serialisasi data, atau situasi lain di mana kamu ingin menghindari alokasi memori yang tidak perlu dalam hot path.
Perbandingan: strconv vs fmt #
Ini adalah pertanyaan yang sangat sering muncul: kapan harus pakai strconv dan kapan pakai fmt?
flowchart TD
Q{"Apa yang ingin\ndilakukan?"} --> Conv["Konversi tipe\n(int↔string, float↔string, bool↔string)"]
Q --> Rich["Format kompleks\n(multiple values, padding, width)"]
Q --> Debug["Debugging atau\nlogging ke konsol"]
Q --> Err["Membuat error\ndengan konteks"]
Conv --> C2{"Seberapa\npenting performa?"}
C2 -- "Hot path / banyak panggilan" --> SC["strconv\n3-5× lebih cepat\ntidak ada alokasi ekstra"]
C2 -- "Biasa saja" --> FMT["fmt.Sprintf\nlebih mudah dibaca"]
Rich --> FMT2["fmt.Sprintf\n'%05d', '%-10s', dll"]
Debug --> FMT3["fmt.Printf / fmt.Println"]
Err --> FMT4["fmt.Errorf dengan %w"]
style SC fill:#e8f5e9
style FMT fill:#e3f2fd
style FMT2 fill:#e3f2fd
style FMT3 fill:#e3f2fd
style FMT4 fill:#e3f2fdimport (
"strconv"
"fmt"
"testing"
)
// Benchmark sederhana untuk ilustrasi
func BenchmarkItoa(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = strconv.Itoa(12345)
}
}
// BenchmarkItoa: ~15 ns/op, 0 allocs/op
func BenchmarkSprintfInt(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = fmt.Sprintf("%d", 12345)
}
}
// BenchmarkSprintfInt: ~70 ns/op, 1 allocs/op
// Kesimpulan: strconv.Itoa sekitar 4-5x lebih cepat untuk konversi int→string
Kapan Masing-masing Lebih Tepat #
// GUNAKAN strconv untuk konversi tunggal
id := 42
idStr := strconv.Itoa(id) // ✓ cepat, jelas
idStr2 := fmt.Sprintf("%d", id) // ✗ overhead tidak perlu
harga := 99.99
hargaStr := strconv.FormatFloat(harga, 'f', 2, 64) // ✓
hargaStr2 := fmt.Sprintf("%.2f", harga) // ✗ untuk konversi saja
// GUNAKAN fmt untuk format yang lebih kaya
label := fmt.Sprintf("ID: %05d | Harga: Rp%,.2f", id, harga) // ✓ fmt lebih cocok
// strconv tidak bisa handle ini dalam satu panggilan
// GUNAKAN strconv untuk parsing input
func parseQueryParam(params url.Values) (*Filter, error) {
filter := &Filter{}
if halStr := params.Get("hal"); halStr != "" {
hal, err := strconv.Atoi(halStr)
if err != nil {
return nil, fmt.Errorf("parameter 'hal' tidak valid: %w", err)
}
filter.Halaman = hal
}
if limitStr := params.Get("limit"); limitStr != "" {
limit, err := strconv.ParseInt(limitStr, 10, 32)
if err != nil {
return nil, fmt.Errorf("parameter 'limit' tidak valid: %w", err)
}
if limit < 1 || limit > 100 {
return nil, fmt.Errorf("limit harus antara 1-100, dapat: %d", limit)
}
filter.Limit = int(limit)
}
return filter, nil
}
Pola Penggunaan di Produksi #
Parser Query Parameter HTTP #
import (
"fmt"
"net/http"
"strconv"
)
type PaginasiParam struct {
Halaman int
Limit int
Urutan string
}
func parsePaginasi(r *http.Request) (*PaginasiParam, error) {
q := r.URL.Query()
param := &PaginasiParam{
Halaman: 1, // default
Limit: 20, // default
Urutan: "asc",
}
if s := q.Get("page"); s != "" {
hal, err := strconv.Atoi(s)
if err != nil || hal < 1 {
return nil, fmt.Errorf("parameter 'page' tidak valid: %q", s)
}
param.Halaman = hal
}
if s := q.Get("limit"); s != "" {
limit, err := strconv.Atoi(s)
if err != nil || limit < 1 || limit > 100 {
return nil, fmt.Errorf("parameter 'limit' tidak valid: %q (harus 1-100)", s)
}
param.Limit = limit
}
if s := q.Get("order"); s == "asc" || s == "desc" {
param.Urutan = s
}
return param, nil
}
func handlerDaftarProduk(w http.ResponseWriter, r *http.Request) {
param, err := parsePaginasi(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// gunakan param...
fmt.Fprintf(w, "Halaman %d, Limit %d, Urutan %s\n",
param.Halaman, param.Limit, param.Urutan)
}
Serialisasi Manual ke CSV #
import (
"strings"
"strconv"
)
type Produk struct {
ID int
Nama string
Harga float64
Aktif bool
Stok int
}
// Konversi produk ke baris CSV tanpa library tambahan
func produkKeCSV(p Produk) string {
var sb strings.Builder
sb.WriteString(strconv.Itoa(p.ID))
sb.WriteByte(',')
sb.WriteString(strconv.Quote(p.Nama)) // handle nama yang mengandung koma
sb.WriteByte(',')
sb.WriteString(strconv.FormatFloat(p.Harga, 'f', 2, 64))
sb.WriteByte(',')
sb.WriteString(strconv.FormatBool(p.Aktif))
sb.WriteByte(',')
sb.WriteString(strconv.Itoa(p.Stok))
return sb.String()
}
// Parse baris CSV kembali ke Produk
func csvKeProduk(baris string) (Produk, error) {
bagian := strings.SplitN(baris, ",", 5)
if len(bagian) != 5 {
return Produk{}, fmt.Errorf("format CSV tidak valid: %q", baris)
}
id, err := strconv.Atoi(bagian[0])
if err != nil {
return Produk{}, fmt.Errorf("ID tidak valid: %w", err)
}
nama, err := strconv.Unquote(bagian[1])
if err != nil {
nama = bagian[1] // fallback jika tidak ada kutip
}
harga, err := strconv.ParseFloat(bagian[2], 64)
if err != nil {
return Produk{}, fmt.Errorf("harga tidak valid: %w", err)
}
aktif, err := strconv.ParseBool(bagian[3])
if err != nil {
return Produk{}, fmt.Errorf("status aktif tidak valid: %w", err)
}
stok, err := strconv.Atoi(bagian[4])
if err != nil {
return Produk{}, fmt.Errorf("stok tidak valid: %w", err)
}
return Produk{
ID: id,
Nama: nama,
Harga: harga,
Aktif: aktif,
Stok: stok,
}, nil
}
Parsing Konfigurasi dari File .env #
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
type AppConfig struct {
Port int
Debug bool
MaxConn int
Timeout float64 // dalam detik
AppName string
}
func muatDotEnv(path string) (map[string]string, error) {
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("muatDotEnv: %w", err)
}
defer f.Close()
hasil := make(map[string]string)
scanner := bufio.NewScanner(f)
nomorBaris := 0
for scanner.Scan() {
nomorBaris++
baris := strings.TrimSpace(scanner.Text())
// Skip baris kosong dan komentar
if baris == "" || strings.HasPrefix(baris, "#") {
continue
}
bagian := strings.SplitN(baris, "=", 2)
if len(bagian) != 2 {
return nil, fmt.Errorf("baris %d: format tidak valid: %q", nomorBaris, baris)
}
key := strings.TrimSpace(bagian[0])
val := strings.TrimSpace(bagian[1])
// Hapus tanda kutip jika ada
if unquoted, err := strconv.Unquote(val); err == nil {
val = unquoted
}
hasil[key] = val
}
return hasil, scanner.Err()
}
func parseConfig(env map[string]string) (*AppConfig, error) {
cfg := &AppConfig{
Port: 8080,
Debug: false,
MaxConn: 10,
Timeout: 30.0,
AppName: "MyApp",
}
if v, ok := env["PORT"]; ok {
port, err := strconv.Atoi(v)
if err != nil {
return nil, fmt.Errorf("PORT tidak valid: %w", err)
}
if port < 1 || port > 65535 {
return nil, fmt.Errorf("PORT harus 1-65535, dapat: %d", port)
}
cfg.Port = port
}
if v, ok := env["DEBUG"]; ok {
debug, err := strconv.ParseBool(v)
if err != nil {
return nil, fmt.Errorf("DEBUG harus boolean: %w", err)
}
cfg.Debug = debug
}
if v, ok := env["MAX_CONN"]; ok {
maxConn, err := strconv.Atoi(v)
if err != nil {
return nil, fmt.Errorf("MAX_CONN tidak valid: %w", err)
}
cfg.MaxConn = maxConn
}
if v, ok := env["TIMEOUT"]; ok {
timeout, err := strconv.ParseFloat(v, 64)
if err != nil {
return nil, fmt.Errorf("TIMEOUT tidak valid: %w", err)
}
cfg.Timeout = timeout
}
if v, ok := env["APP_NAME"]; ok && v != "" {
cfg.AppName = v
}
return cfg, nil
}
Kapan Beralih ke Alternatif #
Tetap gunakan strconv jika:
✓ Konversi antara string dan int, float, bool
✓ Parsing input dari form, query param, file konfigurasi
✓ Serialisasi nilai primitif ke string untuk CSV atau format teks
✓ Hot path yang butuh konversi dengan performa tinggi
✓ Escaping dan unescaping string dengan Quote/Unquote
Pertimbangkan fmt.Sprintf jika:
✗ Format yang lebih kaya: padding, lebar kolom, multiple values
✗ Kode lebih mengutamakan keterbacaan daripada performa
✗ Satu kali format untuk banyak nilai sekaligus
Pertimbangkan encoding/json jika:
✗ Konversi struct ke string (serialisasi JSON)
✗ Data yang kompleks dengan nested structure
✗ Interoperabilitas dengan API atau sistem lain
Pertimbangkan encoding/csv jika:
✗ Membaca atau menulis file CSV yang kompleks
✗ CSV dengan quoting, newline di dalam field, atau escape yang rumit
Ringkasan #
strconv.Atoidanstrconv.Itoaadalah shortcut paling umum — konversi langsung antarastringdaninttanpa overhead format string.- Selalu periksa error dari fungsi Parse* — parse yang gagal bukan panik, tapi mengembalikan zero value dan error yang harus ditangani secara eksplisit.
*strconv.NumErrormemiliki dua jenis:ErrSyntax(format salah) danErrRange(angka valid tapi di luar rentang) — bedakan keduanya untuk pesan error yang lebih informatif.ParseIntdenganbase 0mendeteksi basis secara otomatis dari prefix:0xuntuk hex,0buntuk biner,0untuk oktal — berguna untuk input yang mungkin dalam berbagai format.FormatFloatdenganprec -1menghasilkan representasi minimum yang bisa di-parse kembali ke nilai yang sama persis — gunakan ini untuk round-trip float yang akurat.- Varian
Append*(AppendInt,AppendFloat, dll) menghindari alokasi string baru dengan menambahkan langsung ke[]byte— penting untuk hot path dengan volume tinggi.strconv3-5× lebih cepat darifmt.Sprintfuntuk konversi tipe tunggal — gunakanstrconvdi handler HTTP dan loop yang sering dipanggil.strconv.Quotedanstrconv.Unquoteberguna untuk logging dan debugging string yang mungkin mengandung karakter tak terlihat atau spesial.- Validasi setelah parsing — jangan hanya periksa error parsing, validasi juga rentang nilai (misalnya port 1-65535, halaman > 0) sebelum menggunakan nilai yang diparsing.