Ответ
В Go строка — это неизменяемая последовательность байт в кодировке UTF-8. Из-за этого существуют два принципиально разных способа итерации с разным результатом.
1. Итерация с помощью for range
(по рунам)
Этот способ является предпочтительным для работы с символами. Цикл for range
автоматически декодирует UTF-8 последовательности в руны (тип rune
, который является псевдонимом для int32
).
- Что возвращает: Индекс начала руны в байтовой последовательности и саму руну.
- Преимущество: Корректно обрабатывает многобайтовые символы (кириллица, эмодзи и т.д.).
s := "你好, Go!"
for i, r := range s {
// i будет 0, 3, 6, 7, 8, 9, 10
// r будет '你', '好', ',', ' ', 'G', 'o', '!'
fmt.Printf("Индекс байта: %d, Руна: %cn", i, r)
}
2. Итерация по индексу (по байтам)
Этот способ работает напрямую с байтами, из которых состоит строка.
- Что возвращает: Индекс байта и сам байт (тип
byte
, псевдоним дляuint8
). - Проблема: Для многобайтовых символов этот подход вернет части символа, что приведет к некорректному результату.
s := "Go"
for i := 0; i < len(s); i++ {
fmt.Printf("Индекс: %d, Байт: %vn", i, s[i])
}
// Для строки "Go" результат будет корректным, т.к. символы однобайтовые.
// Для строки "你好" результат будет неверным.
Ключевые моменты:
len(s)
возвращает количество байт в строке, а не символов.- Для подсчета количества рун (символов) используйте
utf8.RuneCountInString(s)
.