String di Go adalah rangkaian byte, dan source code serta literal string Go memakai UTF-8. byte adalah salah satu byte itu. rune adalah satu karakter Unicode (sebuah code point), yang disimpan UTF-8 dalam 1 sampai 4 byte. Sebagian besar kebingungan soal string di Go berasal dari tertukarnya keduanya:
語 berukuran tiga byte di UTF-8, jadi "語Go" berukuran 5 byte tapi berisi 3 rune. Loop indeks melihat lima byte, dan byte 0, 1, dan 2 tidak bermakna kalau berdiri sendiri. Loop range men-decode UTF-8 dan menghasilkan tiga rune di indeks 0, 3, dan 4: i selalu merupakan offset byte, bukan jumlah karakter.
byte dan rune adalah alias
| Nama | Tipe yang sama dengan | Literal | Arti |
|---|---|---|---|
byte | uint8 | byte('A'), 0x41 | satu byte data |
rune | int32 | 'A', 'é', '世' | satu code point Unicode |
Karena keduanya alias, byte adalah uint8 dan rune adalah int32; tidak perlu konversi antara alias dan tipe dasarnya. Tanda kutip tunggal menghasilkan rune, tanda kutip ganda menghasilkan string. 'ab' adalah compile error, karena literal rune berisi tepat satu karakter.
Rune adalah angka, jadi kamu bisa melakukan aritmetika padanya:
'7' - '0' adalah cara klasik untuk mengubah karakter digit menjadi nilainya. Package unicode mengklasifikasikan rune (IsLetter, IsDigit, IsSpace, IsUpper) dan mengubah huruf besar dan kecil dengan benar untuk huruf non-ASCII. %c mencetak rune sebagai karakter, %U dalam bentuk U+4E16, %q sebagai literal rune bertanda kutip.
Cara UTF-8 menyimpan karakter
| Code point | Byte | Contoh |
|---|---|---|
| U+0000 sampai U+007F | 1 | ASCII: a, 7, { |
| U+0080 sampai U+07FF | 2 | é, ß, ж, ع |
| U+0800 sampai U+FFFF | 3 | 語, €, 한 |
| U+10000 ke atas | 4 | emoji, CJK yang langka |
Teks ASCII sama persis di UTF-8, itulah sebabnya kode berbasis byte sering berjalan lancar saat diuji lalu rusak pada nama pertama yang memakai aksen. Konverter teks ke biner menampilkan byte UTF-8 dari teks apa pun yang kamu tempel. Package unicode/utf8 bekerja langsung dengan encoding-nya:
% x (dengan spasi) mencetak byte dalam heksadesimal yang dipisahkan spasi. utf8.ValidString melaporkan apakah sebuah string berisi UTF-8 yang valid. String Go boleh berisi byte apa pun, tidak hanya UTF-8 yang valid; saat range bertemu byte yang tidak valid, ia menghasilkan utf8.RuneError (U+FFFD, karakter pengganti) lalu maju satu byte.
Konversi antara string, []byte, dan []rune
| Konversi | Hasil | Biaya |
|---|---|---|
[]byte(s) | byte UTF-8-nya | menyalin |
[]rune(s) | code point hasil decode | men-decode dan menyalin (4 byte per rune) |
string(b) | string dari byte | menyalin |
string(r) dengan r berupa []rune | meng-encode ke UTF-8 | menyalin |
string(r) dengan r berupa rune | string berisi satu karakter | kecil |
Membalik berdasarkan byte akan mengacak 世界 menjadi UTF-8 yang tidak valid, itulah sebabnya reverse bekerja pada rune. Bahkan rune pun belum menceritakan semuanya: karakter seperti é juga bisa ditulis sebagai e ditambah aksen kombinasi, yaitu dua rune. Untuk karakter yang terlihat oleh pengguna (grapheme cluster), pakai library seperti github.com/rivo/uniseg.
Setiap konversi melakukan penyalinan, karena string bersifat immutable sedangkan slice tidak. Di kode yang sering dieksekusi, hindari konversi bolak-balik; compiler mengoptimalkan beberapa kasus (seperti string(b) yang dipakai sebagai key map atau dalam perbandingan) untuk melewati penyalinan.
Indeks menghasilkan byte
s[i] mengembalikan byte, dan s[i:j] memotong berdasarkan offset byte:
s := "café"
fmt.Println(s[3]) // 195: the first byte of é
fmt.Println(string(s[3])) // "Ã": that byte read as a code point
fmt.Println(s[:3]) // "caf"
fmt.Println(s[:4]) // "caf\xc3": half of é, invalid UTF-8
string(s[3]) adalah jebakan: mengubah satu byte menjadi string memperlakukannya sebagai code point 195, yaitu Ã, bukan byte itu sendiri. Untuk menemukan batas karakter, pakai range, keluarga strings.Index (yang mengembalikan offset byte yang selalu tepat di batas karakter), atau utf8.DecodeRuneInString.
Package bytes
bytes adalah cerminan strings untuk []byte: bytes.Contains, bytes.Split, bytes.Fields, bytes.TrimSpace, bytes.Equal, bytes.ToUpper, dan seterusnya. Pakai package ini saat data datang dalam bentuk byte (isi file, body HTTP, buffer jaringan), supaya kamu tidak perlu mengubahnya menjadi string lalu kembali lagi:
bytes.Buffer adalah buffer byte yang bisa membesar dan mengimplementasikan io.Reader serta io.Writer. Ini alat yang tepat saat kamu menulis sekaligus membaca byte; untuk menyusun string, strings.Builder sedikit lebih murah. Bandingkan byte slice dengan bytes.Equal, karena == tidak bisa dipakai pada slice.
Memilih di antara keduanya
- Teks yang kamu tampilkan, bandingkan, atau cari: biarkan sebagai
string, dan pakairangesaat menelusurinya karakter demi karakter. - Posisi karakter, membalik, memotong menjadi n karakter: ubah menjadi
[]rune. - I/O, hashing, encoding, protokol biner: pakai
[]bytedan packagebytes. - Satu karakter:
rune. Satu byte mentah:byte.
Pertanyaan yang sering diajukan
Apa itu rune di Go?
rune adalah alias untuk int32 dan mewakili satu code point Unicode. Literal rune ditulis dengan tanda kutip tunggal: 'a' bernilai 97, 'é' bernilai 233, '世' bernilai 19990. Melakukan range pada string dengan for i, r := range s memberimu rune, hasil decode dari byte UTF-8 string itu.
Apa perbedaan rune dan byte di Go?
byte (alias untuk uint8) adalah 8 bit data mentah. rune (alias untuk int32) adalah satu karakter Unicode utuh. Di string UTF-8, karakter ASCII masing-masing memakai satu byte, sedangkan karakter lain memakai dua sampai empat byte tapi tetap satu rune. len(s) menghitung byte; utf8.RuneCountInString(s) menghitung rune.
Bagaimana cara melakukan perulangan pada karakter sebuah string di Go?
Pakai for i, r := range s. Setiap iterasi memberikan offset byte i dan rune r, jadi karakter multi-byte ditangani dengan benar. Loop indeks biasa for i := 0; i < len(s); i++ mengunjungi byte, yang memotong karakter non-ASCII menjadi kepingan.
Bagaimana cara mengubah string menjadi byte slice di Go?
b := []byte(s) menyalin byte string itu ke slice baru. string(b) mengubahnya kembali, juga dengan menyalin. Pakai []rune(s) sebagai gantinya saat kamu perlu bekerja dengan karakter, misalnya untuk membalik string atau mengambil n karakter pertamanya.