Какие есть нюансы при итерации по строке?go-20

Основные способы итерации

1. Итерация по байтам

s := "Привет"
for i := 0; i < len(s); i++ {
    fmt.Printf("Byte %d: %v\n", i, s[i])
}

Особенности:

  • Работает на уровне байтов
  • Для UTF-8 выводит отдельные байты многобайтовых символов
  • Не подходит для Unicode текста

2. Итерация по рунам

s := "Привет"
for index, runeValue := range s {
    fmt.Printf("Rune %d: %#U\n", index, runeValue)
}

Особенности:

  • Корректно обрабатывает UTF-8
  • index - позиция первого байта руны в строке
  • runeValue - кодовая точка Unicode (int32)

3. Явное преобразование в []rune

s := "Привет"
runes := []rune(s)
for i, r := range runes {
    fmt.Printf("Rune %d: %c\n", i, r)
}

Особенности:

  • Создает копию данных в памяти
  • Позволяет обращаться к рунам по индексу
  • Индексы соответствуют порядковому номеру символа

Ключевые нюансы

1. Комбинированные символы

s := "é"  // 'e' + combining acute accent
for i, r := range s {
    fmt.Printf("%d: %#U\n", i, r)  // Выведет две отдельные руны
}

Проблема: Визуально один символ, но технически две руны

Решение: Использовать нормализацию Unicode:

import "golang.org/x/text/unicode/norm"

s := norm.NFC.String("é")  // Нормализует в одну руну

2. Модификация строки при итерации

s := "hello"
for _, r := range s {
    r = 'a'  // Не изменяет исходную строку!
}

Важно: Строки в Go неизменяемы. Для модификации нужно:

  1. Преобразовать в []rune
  2. Модифицировать
  3. Преобразовать обратно в string

3. Эмодзи и сложные символы

s := "😊♂"  // Смайлик + гендерный символ
for i, r := range s {
    fmt.Printf("%d: %c\n", i, r)  // Корректно обработает
}

4. Пустые строки и спецсимволы

s := "\x00Привет\n"  // С нулевым байтом и переводом строки
for _, r := range s {
    if r == 0 {
        fmt.Println("Found null byte!")
    }
}

5. Производительность

  • range по строке: оптимально для UTF-8
  • []rune: дополнительная аллокация памяти
  • Прямая индексация: только для ASCII или байтовых операций

Ошибки при итерации

1. Неправильный подсчет символов

s := "Привет"
count := len(s)  // 12 вместо 6!

2. Попытка модификации

s := "hello"
for i := range s {
    s[i] = 'a'  // Ошибка компиляции!
}

3. Потеря позиции байтов

s := "Привет"
runes := []rune(s)
pos := runes[3]  // Позиция в рунах ≠ позиция в байтах

Резюмируем

  1. Для Unicode текста всегда используйте for range
  2. Помните о разнице между позицией байта и индексом руны
  3. Комбинированные символы требуют нормализации
  4. Строки неизменяемы - для модификации нужны преобразования
  5. []rune создает копию данных, но удобен для работы
  6. Прямая индексация (s[i]) работает только с байтами
  7. Спецсимволы и нулевые байты обрабатываются корректно

Выбор метода итерации зависит от задачи:

  • Анализ байтов? Используйте прямую индексацию
  • Работа с Unicode? Используйте for range
  • Нужны модификации? Преобразуйте в []rune
  • Комбинированные символы? Применяйте нормализацию