Bir Go string'i bir bayt dizisidir; Go kaynak kodu ve string literal'leri UTF-8'dir. Bir byte bu baytlardan biridir. Bir rune ise UTF-8'in 1 ila 4 baytta sakladığı tek bir Unicode karakteridir (bir kod noktası). Go'daki string karışıklığının çoğu ikisini birbirine karıştırmaktan gelir:
語 UTF-8'de üç bayttır, bu yüzden "語Go" 5 bayt ama 3 rune'dur. İndeks döngüsü beş bayt görür ve 0, 1 ve 2 numaralı baytlar tek başlarına anlamsızdır. range döngüsü UTF-8'i çözer ve 0, 3 ve 4 indekslerinde üç rune verir: i her zaman bir karakter sayısı değil, bir bayt konumudur.
byte ve rune Takma Addır
| Ad | Aynı tip | Literal | Anlamı |
|---|---|---|---|
byte | uint8 | byte('A'), 0x41 | bir baytlık veri |
rune | int32 | 'A', 'é', '世' | bir Unicode kod noktası |
Takma ad oldukları için bir byte bir uint8, bir rune bir int32'dir; takma ad ile temel tipi arasında dönüşüm gerekmez. Tek tırnak bir rune, çift tırnak bir string oluşturur. 'ab' bir derleme hatasıdır, çünkü bir rune literal'i tam olarak bir karakter tutar.
Rune'lar sayıdır, bu yüzden üzerlerinde aritmetik yapabilirsiniz:
'7' - '0', bir rakam karakterini değerine çevirmenin klasik yoludur. unicode paketi rune'ları sınıflandırır (IsLetter, IsDigit, IsSpace, IsUpper) ve ASCII olmayan harfler için büyük/küçük harfi doğru değiştirir. %c bir rune'u karakter olarak, %U U+4E16 biçiminde, %q tırnaklı bir rune literal'i olarak yazdırır.
UTF-8 Karakterleri Nasıl Saklar
| Kod noktaları | Bayt | Örnekler |
|---|---|---|
| U+0000 ile U+007F arası | 1 | ASCII: a, 7, { |
| U+0080 ile U+07FF arası | 2 | é, ß, ж, ع |
| U+0800 ile U+FFFF arası | 3 | 語, €, 한 |
| U+10000 ve üstü | 4 | emoji, nadir CJK |
ASCII metin UTF-8'de aynıdır; bayt tabanlı kodun testte çoğu zaman çalışıp ilk aksanlı adda bozulmasının nedeni budur. unicode/utf8 paketi doğrudan kodlamayla çalışır:
% x (boşlukla) baytları boşluklarla ayrılmış onaltılık olarak yazdırır. utf8.ValidString bir string'in geçerli UTF-8 olup olmadığını bildirir. Go string'leri yalnızca geçerli UTF-8 değil, herhangi bir bayt tutabilir; range geçersiz bir baytla karşılaştığında utf8.RuneError (U+FFFD, yedek karakter) verir ve bir bayt ilerler.
string, []byte ve []rune Arasında Dönüşüm
| Dönüşüm | Sonuç | Maliyet |
|---|---|---|
[]byte(s) | UTF-8 baytları | kopyalar |
[]rune(s) | çözülmüş kod noktaları | çözer ve kopyalar (rune başına 4 bayt) |
string(b) | baytlardan bir string | kopyalar |
r bir []rune iken string(r) | UTF-8'e kodlar | kopyalar |
r bir rune iken string(r) | tek karakterli bir string | küçük |
Baytlara göre ters çevirmek 世界'yi geçersiz UTF-8'e karıştırırdı; reverse'ün rune'lar üzerinde çalışmasının nedeni budur. Rune'lar bile hikâyenin tamamı değildir: é gibi bir karakter e artı birleşen bir aksan olarak da yazılabilir, bu da iki rune'dur. Kullanıcıya görünen karakterler (grapheme cluster'lar) için github.com/rivo/uniseg gibi bir kütüphane kullanın.
Her dönüşüm kopyalar, çünkü string'ler değiştirilemez, slice'lar değiştirilebilirdir. Sık çalışan kodda ileri geri dönüştürmekten kaçının; derleyici bazı durumları (map anahtarı ya da bir karşılaştırmada kullanılan string(b) gibi) kopyayı atlayacak şekilde optimize eder.
İndeksleme Bir Bayt Verir
s[i] bir byte döndürür ve s[i:j] bayt konumlarına göre dilimler:
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]) bir tuzaktır: tek bir baytı string'e dönüştürmek onu baytın kendisi olarak değil, kod noktası 195 olan à olarak ele alır. Karakter sınırlarını bulmak için range'i, strings.Index ailesini (her zaman sınırlarda olan bayt konumları döndürürler) ya da utf8.DecodeRuneInString'i kullanın.
bytes Paketi
bytes, []byte için strings'in aynısıdır: bytes.Contains, bytes.Split, bytes.Fields, bytes.TrimSpace, bytes.Equal, bytes.ToUpper ve diğerleri. Veri bayt olarak geldiğinde (dosya içerikleri, HTTP gövdeleri, ağ buffer'ları) onu kullanın, böylece string'e çevirip geri dönüştürmezsiniz:
bytes.Buffer, io.Reader ve io.Writer'ı gerçekleştiren büyüyebilen bir bayt buffer'ıdır. Baytları hem yazıp hem okuduğunuzda doğru araçtır; bir string oluşturmak için strings.Builder biraz daha ucuzdur. == slice'larda çalışmadığı için byte slice'larını bytes.Equal ile karşılaştırın.
Aralarında Seçim Yapmak
- Gösterdiğiniz, karşılaştırdığınız ya da aradığınız metin: onu bir
stringolarak tutun ve karakter karakter dolaşırkenrangekullanın. - Karakter konumları, ters çevirme, n karaktere kırpma:
[]rune'a dönüştürün. - G/Ç, hash'leme, kodlama, ikili protokoller:
[]bytevebytespaketini kullanın. - Tek bir karakter: bir
rune. Tek bir ham bayt: birbyte.
Sıkça Sorulan Sorular
Go'da rune nedir?
rune, int32 için bir takma addır ve bir Unicode kod noktasını temsil eder. Bir rune literal'i tek tırnakla yazılır: 'a' 97, 'é' 233, '世' 19990'dır. Bir string üzerinde for i, r := range s ile dönmek, string'in UTF-8 baytlarından çözülmüş rune'lar verir.
Go'da rune ile byte arasındaki fark nedir?
Bir byte (uint8 için takma ad) 8 bitlik ham veridir. Bir rune (int32 için takma ad) bütün bir Unicode karakteridir. Bir UTF-8 string'inde ASCII karakterler birer bayt tutar, diğer karakterler iki ila dört bayt tutar ama yine de tek bir rune'dur. len(s) baytları, utf8.RuneCountInString(s) rune'ları sayar.
Go'da bir string'in karakterleri üzerinde nasıl dönülür?
for i, r := range s kullanın. Her yineleme bayt konumu i'yi ve rune r'yi verir, bu yüzden çok baytlı karakterler doğru ele alınır. Düz bir indeks döngüsü for i := 0; i < len(s); i++ baytları ziyaret eder ve ASCII olmayan karakterleri parçalara böler.
Go'da bir string nasıl byte slice'ına dönüştürülür?
b := []byte(s), string'in baytlarını yeni bir slice'a kopyalar. string(b) yine bir kopyayla geri dönüştürür. Karakterlerle çalışmanız gerektiğinde, örneğin bir string'i ters çevirmek ya da ilk n karakterini almak için, bunun yerine []rune(s) kullanın.