fmt.Println(len("Привет"))?В Go строка (string) - это неизменяемая последовательность байт, а не символов. Функция len() возвращает именно количество байт.
Кириллические символы в UTF-8 занимают по 2 байта каждый. Слово "Привет" состоит из 6 букв, но из 12 байт:
s := "Привет"
fmt.Println(len(s)) // 12 (байт, не символов)
// Побайтовый вывод:
for i := 0; i < len(s); i++ {
fmt.Printf("%d: 0x%X\n", i, s[i])
}
// 0: 0xD0 1: 0x9F (П)
// 2: 0xD1 3: 0x80 (р)
// ...
Есть два способа подсчитать именно символы (руны):
fmt.Println(utf8.RuneCountInString(s)) // 6
fmt.Println(len([]rune(s))) // 6
Первый способ эффективнее - он не создает новый слайс, а просто считает руны.
for i, r := range "Привет" {
fmt.Printf("байт %d: %c (U+%04X)\n", i, r, r)
}
// байт 0: П (U+041F)
// байт 2: р (U+0440)
// байт 4: и (U+0438)
// байт 6: в (U+0432)
// байт 8: е (U+0435)
// байт 10: т (U+0442)
Обратите внимание: индекс i увеличивается на 2 (не на 1), потому что каждая руна занимает 2 байта.
len(string) - количество байтlen([]rune(string)) - количество символов (рун)len([]byte{...}) - количество байтlen(slice) - количество элементовЛатинские буквы и цифры в UTF-8 занимают 1 байт, поэтому для ASCII-строк len() совпадает с количеством символов. Ловушка проявляется именно на многобайтовых символах: кириллица (2 байта), китайские иероглифы (3 байта), эмодзи (4 байта).