12. String

String adalah nilai yang paling sering kamu cetak. Bab 07 menunjukkan dua cara menulisnya: di antara tanda kutip ganda, "seperti ini", dan di antara backtick, `seperti ini`. Bab ini membahas apa yang disediakan Go secara langsung untuk string: menggabungkan, mengukur, membaca satu posisi, memotong sebagian, dan menulis karakter khusus.

Menggabungkan string dengan +

Kamu bisa menggabungkan dua string dengan + (operator concatenation). Hasilnya satu string baru dengan string kedua menempel di akhir string pertama:

first := "Hello, "
second := "world!"
fmt.Println(first + second) // Hello, world!

Variabel aslinya tidak berubah — + membangun string baru.

+ di Go tidak mengubah angka menjadi string. Menambahkan angka ke string adalah error kompilasi:

level := 7
fmt.Println("Level " + level)
invalid operation: "Level " + level (mismatched types untyped string and int)

Untuk sekarang, perbaikannya adalah fmt.Printf dari Bab 09, yang menerima angka secara langsung:

level := 7
fmt.Printf("Level %d complete\n", level) // Level 7 complete

Bab 14 menunjukkan cara mengubah angka menjadi string, kalau kamu membutuhkannya.

Panjang string dengan len

Fungsi len memberikan panjang sebuah string:

fmt.Println(len("hello")) // 5
fmt.Println(len(""))      // 0
greeting := "Welcome"
fmt.Println(len(greeting)) // 7

Gunakan len setiap kali kamu butuh panjang string — untuk menengahkan teks, memeriksa panjang input, atau membuat border.

Buka exercises/12/01-length.go. Tambahkan baris yang mencetak panjang namamu sendiri.

String adalah byte

Bagian sebelumnya belum menyebutkan apa yang dihitung len. Yang dihitung adalah byte, satuan kecil yang dipakai komputer untuk menyimpan teks. Huruf Inggris biasa, angka, atau spasi memakai satu byte, jadi untuk teks seperti "hello", jumlah byte dan jumlah karakternya sama. Huruf beraksen memakai lebih banyak:

fmt.Println(len("e")) // 1
fmt.Println(len("é")) // 2

Untuk menghitung karakter dan bukan byte, gunakan RuneCountInString dari package unicode/utf8. Rune adalah sebutan Go untuk satu karakter:

word := "café"
fmt.Println(len(word))                    // 5
fmt.Println(utf8.RuneCountInString(word)) // 4

Ini butuh import kedua. Untuk mengimpor lebih dari satu package, taruh di dalam tanda kurung, satu per baris:

import (
    "fmt"
    "unicode/utf8"
)

Program lengkapnya ada di exercises/12/03-bytes.go. Sebagian besar teks di buku ini berupa huruf Inggris biasa, jadi sisa Bagian 3 memakai len. Ingat saja bahwa hasilnya bisa lebih besar daripada jumlah karakter yang kamu lihat.

Indeks

Setiap byte dalam string punya posisi, yang disebut indeks. Kurung siku membaca byte di satu posisi:

word := "dragon"
fmt.Println(word[0]) // 100

Ada dua hal yang mengejutkan di sini. Indeks dimulai dari 0, bukan 1, jadi word[0] adalah posisi pertama. Dan hasilnya bukan huruf d, melainkan 100, angka yang menyimpan d, karena indeks memberimu sebuah byte. Untuk mendapatkan teks, gunakan slice, yang dibahas di bagian berikutnya.

String di Go berindeks mulai dari 0. Kalau kamu menginginkan posisi pertama, gunakan word[0], bukan word[1]. word[1] memberikan posisi kedua. Awalnya ini akan terasa salah, tapi cepat menjadi terbiasa.

Slice

Slice dari sebuah string memberimu sebagian isinya, sebagai string baru. Sintaksnya s[start:stop], dengan start adalah indeks byte pertama yang kamu inginkan dan stop adalah indeks setelah byte terakhir:

word := "dragon"
fmt.Println(word[0:3]) // dra
fmt.Println(word[3:])  // gon
fmt.Println(word[:1])  // d

Menghilangkan stop berarti "sampai akhir string", dan menghilangkan start berarti "dari awal". Jadi word[:1] adalah huruf pertama sebagai string.

Go tidak punya indeks negatif. word[-3:] berhenti dengan invalid argument: index -3 (constant of type int) must not be negative. Untuk menghitung dari akhir, gunakan len:

word := "dragon"
fmt.Println(word[len(word)-3:]) // gon
fmt.Println(word[len(word)-1:]) // n

Meminta posisi yang melewati akhir string adalah error yang biasanya tidak bisa ditangkap compiler, jadi terjadi saat program berjalan. Program berhenti dan mencetak pesan seperti:

panic: runtime error: slice bounds out of range [:10] with length 6

Slice menghitung byte, seperti len dan indeks. Dalam "café", é memakai dua byte, di indeks 3 dan 4. word[:3] memberikan caf, tapi word[:4] memotong é menjadi dua dan program mencetak karakter yang rusak. Untuk huruf Inggris biasa, hal ini tidak pernah terjadi.

Buka exercises/12/02-index-and-slice.go. Sebuah variabel menyimpan satu kata. Dengan slice, cetak huruf pertamanya saja, lalu huruf terakhirnya saja.

String tidak bisa diubah

Kamu bisa memberi variabel string sebuah string baru, tapi kamu tidak bisa mengubah byte di dalam string:

word := "dragon"
word[0] = 'D'
fmt.Println(word)
cannot assign to word[0] (neither addressable nor a map index expression)

Untuk mendapatkan versi yang berubah, bangun string baru dari potongan-potongannya dan berikan ke variabel:

word := "dragon"
word = "D" + word[1:]
fmt.Println(word) // Dragon

Escape sequence

Beberapa karakter sulit ditulis dalam string karena bentrok dengan tanda baca Go. Solusinya adalah escape sequence — garis miring terbalik \ diikuti sebuah karakter, yang diganti Go dengan karakter khusus:

Urutan Hasilnya
\n baris baru (pindah ke baris berikutnya)
\t tab
\" tanda kutip ganda
\\ garis miring terbalik

Tanda kutip tunggal tidak perlu di-escape di dalam tanda kutip ganda, jadi tulis "it's". Menulis "it\'s" berhenti dengan unknown escape.

Contoh:

fmt.Println("Line 1\nLine 2\nLine 3")
fmt.Println("Name\tLevel\tHP")
fmt.Println("She said \"hi\" and waved.")

Output:

Line 1
Line 2
Line 3
Name    Level   HP
She said "hi" and waved.

String beberapa baris

Kalau string terdiri dari banyak baris dengan banyak tanda kutip, escape sequence jadi berantakan. String di antara backtick adalah raw string: apa pun di antara keduanya adalah isi string, termasuk baris baru dan tanda kutip, dan escape sequence tidak diganti.

poem := `
Roses are red,
Violets are blue,
"Pick a number,"
said the program to you.
`

fmt.Println(poem)

Setelah dibuat, raw string hanyalah string biasa — dengan cara penulisan yang lebih mudah. String ini diawali dan diakhiri baris baru, karena backtick-nya ada di baris sendiri. Contoh-contoh ini ada di exercises/12/04-escapes.go.

Karena \n tidak diganti dalam raw string, `a\nb` mencetak empat karakter a\nb dalam satu baris. Ini membuat backtick praktis untuk teks dengan banyak garis miring terbalik, seperti path Windows.

PR

Soal 1 — Inisial

Buka exercises/12/homework/01-initials.go. Dua variabel menyimpan nama depan dan nama belakang. Dengan slice, cetak inisial orang itu, seperti K.R. untuk "Keiko Raharja".

Soal 2 — Baris stat

Buka exercises/12/homework/02-stat-line.go. Tiga variabel dideklarasikan di bagian atas: string name, int level, int hp. Dengan fmt.Printf, cetak satu baris persis dalam bentuk ini:

Keiko  Lv 7   HP 95

Label (Lv, HP) dan jarak antar bagian adalah spasi biasa di dalam string yang kamu berikan ke fmt.Printf.

Soal 3 — Puisi tiga baris dalam satu panggilan

Buka exercises/12/homework/03-three-line-poem.go. Cetak puisi tiga baris dengan tepat satu panggilan fmt.Println. Pergantian barisnya harus berasal dari \n di dalam string. Tambahkan komentar // setelah panggilan itu yang menjelaskan mengapa satu panggilan bisa menghasilkan tiga baris.

Tantangan — Huruf terakhir

Buka exercises/12/homework/04-last-letters.go. Sebuah variabel menyimpan satu kata. Dengan slice dan len, cetak tiga karakter terakhirnya, lalu cetak panjangnya. Buat supaya berfungsi untuk kata apa pun yang terdiri dari huruf biasa, minimal tiga huruf, tanpa menghitung manual.

Stuck atau sudah selesai? Buka halaman solusi PR.