Низът в Go е непроменяем отрязък от байтове. Езикът и
стандартната му библиотека работят с низовете
по-особено – сякаш съдържат текст кодиран в
UTF-81. В други езици низовете са направени от
„знаци“. В Го за понятието „знак“ се използва думата
руна – rune. Руната е цяло число, което представлява
кодова точка в таблицата Уникод2. Статията
„Низове, байтове, руни и знаци“
(https://go.dev/blog/strings) е добро въведение по
въпроса. TODO: Да преведа и добавя като приложение към
книгата статията https://go.dev/blog/strings
import (
"fmt"
"unicode/utf8"
)
func main() {
s е string, на който е присвоена буквална
стойност, съдържаща думата „здравей“ на тайски.
Буквалните низови стойности в Go са кодиран в
UTF-8 текст. Това означава, че и изходният
програмен код в Го е такъв текст.
const s = "สวัสดี"
Тъй като низовете са в същността си []byte –
отрязък от байтове, следното изявление ще изведе
броя на суровите байтове в низа.
fmt.Println("дължина в байтове:", len(s))
Ако обходим низа байт по байт по следния начин,
можем да видим шестнадесетичните стойности на
байтовете, от който е съставен низът s.
for i := 0; i < len(s); i++ {
fmt.Printf("%x ", s[i])
}
fmt.Println()
За да преброим колко руни (знакове) има в низа,
можем да ползваме пакета utf8. Забележете, че
времето за изпълнение на RuneCountInString зависи
от размера на низа, защото трябва да се разкодира
от UTF-8 всяка буква (руна) последователно. Някои
знаци в тайски съдържат повече от един байт, когато
са представени с кодови точки в UTF-8. Така е и с
български – буквите ни са съставени от двубайтови
последователности. Така че изходът от следното
преброяване може да ви изненада.
fmt.Println("Брой знаци:", utf8.RuneCountInString(s))
range обхожда низовете по особен начин – като
разкодира всеки знак заедно с отстоянието му в низа
– мястото на байта, от който започва знака.
for idx, runeValue := range s {
fmt.Printf("%#U започва от %d\n", runeValue, idx)
}
Можем да постигнем същото, като използваме функцията utf8.DecodeRuneInString изрично.
fmt.Println("\nИзползваме DecodeRuneInString")
for i, w := 0, 0; i < len(s); i += w {
runeValue, width := utf8.DecodeRuneInString(s[i:])
fmt.Printf("%#U започва от %d\n", runeValue, i)
w = width
Тук показваме подаването на стойност от вид
rune на функция.
examineRune(runeValue)
}
}
func examineRune(r rune) {
Оградените в единични кавички стойности са буквална стойност на руна. Можем да сравним стойност от вида руна с буквална таква стойност направо.
if r == 't' {
fmt.Println("намерих U+0074 LATIN SMALL LETTER T")
} else if r == 'ส' {
fmt.Println("намерих U+0E2A THAI CHARACTER SO SUA")
}
}
$ go run strings-and-runes.go
дължина в байтове: 18
e0 b8 aa e0 b8 a7 e0 b8 b1 e0 b8 aa e0 b8 94 e0 b8 b5
Брой знаци: 6
U+0E2A 'ส' започва от 0
U+0E27 'ว' започва от 3
U+0E31 'ั' започва от 6
U+0E2A 'ส' започва от 9
U+0E14 'ด' започва от 12
U+0E35 'ี' започва от 15
Използваме DecodeRuneInString
U+0E2A 'ส' започва от 0
намерих U+0E2A THAI CHARACTER SO SUA
U+0E27 'ว' започва от 3
U+0E31 'ั' започва от 6
U+0E2A 'ส' започва от 9
намерих U+0E2A THAI CHARACTER SO SUA
U+0E14 'ด' започва от 12
U+0E35 'ี' започва от 15
Следващ пример: Структури.