Функция len golang символами UTF-8
package main
import "fmt"
// import "unsafe"
func main() {
word := string("крен")
// lastSymb := string(word[len(word)-1])
// fmt.Println(lastSymb)
// fmt.Println(word)
// fmt.Println(unsafe.Sizeof(word))
fmt.Println(len(word))
}
Началось с того, что я не мог взять последний символ у русского слова, оказывается, многие источники неверно трактуют возвращаемое значение функции len() ,видимо переводят с английского,в котором все буквы попадают в классическую ASCII.ВНИМАНИЕ функция возвращает длину строки в БАЙТАХ, а не в символах. Вопросы такие:
- Почему возвращает 8 для слова крен, а не 10, где же символ конца строки?
- Какой есть элегантный способ получать длину ЛЮБОЙ строки в символах?
UPD Нашел с использованием либы "unicode/utf8" , но неужели мне надо притягивать целую либу просто для такого простого действия???Есть другие варианты?
Ответы (1 шт):
len возвращает число элементов в массиве или мапе. Тип string по определению это массив байтов фиксированной длины. Поэтому len(word) возвращает 8 байтов - ровно столько занимает представление слова крен в utf-8. В go строки НЕ zero-terminated, поэтому len возвращает 8, а не 9.
Буквы в go представляются типом rune. Для string определено специальное приведение типов []rune(), которое возвращает массив букв строки (32-х битных символов UCS -2)
Приведение к массиву букв включает разбор байтовых последовательностей и запись 4х байтовых рун в память. Функция utf8.RuneCountInString подсчитывает юникодные символы без записи в память. Поэтому, если вам нужно только число символов, эта функция будет быстрее, чем len([]rune(txt)).
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
txt := "крен"
fmt.Println("len:", len(txt))
fmt.Println("len([]rune):", len([]rune(txt)))
fmt.Println("Rune count", utf8.RuneCountInString(txt))
}
https://go.dev/play/p/mYbjJpMeRDz
UPD
Последний символ можно добыть двумя способами - полным перекодированием в массив юникодных символов (ака []rune) и декодированием только последнего символа
// первый способ - конвертировать в последовательность юникодных символов
runes := []rune(txt)
lastRune := runes[len(runes)-1]
fmt.Println("последняя буква: ", string(lastRune))
// второй способ - читать байты с конца, пока не получится валидный utf-8
decodedLastRune, _ := utf8.DecodeLastRuneInString(txt)
fmt.Println("последняя буква: ", string(decodedLastRune))