Изменение строк и итоговые концепции

1. Изменение строки через []rune

Строки иммутабельны — нельзя изменить напрямую.

Паттерн изменения

original := "Кот"
fmt.Printf("До изменения: %s\n", original)

// 1. Преобразуем в срез рун
runeSlice := []rune(original)

// 2. Изменяем элементы
runeSlice[0] = 'К'
runeSlice[1] = 'И'
runeSlice[2] = 'Т'

// 3. Конвертируем обратно в строку
modified := string(runeSlice)
fmt.Printf("После изменения: %s\n", modified)

Вывод:

До изменения: Кот
После изменения: КИТ

Механизм: Преобразование → изменение → обратное преобразование.


2. Практические примеры изменения

Замена символа

text := "Hello"
runes := []rune(text)

runes[0] = 'J'  // H → J

result := string(runes)
fmt.Println(result)  // "Jello"

Вставка символа (сложнее)

text := "Go"
runes := []rune(text)

// Вставить 'o' между 'G' и 'o'
runes = append(runes[:1], append([]rune{'o'}, runes[1:]...)...)

result := string(runes)
fmt.Println(result)  // "Goo"

Удаление символа

text := "Привет"
runes := []rune(text)

// Удалить символ с индексом 2 ('и')
runes = append(runes[:2], runes[3:]...)

result := string(runes)
fmt.Println(result)  // "Првет"

3. Итоговая демонстрация

demo := "Go поддерживает Unicode! 🚀"

fmt.Printf("Строка: %s\n", demo)
fmt.Printf("Длина в байтах: %d\n", len(demo))
fmt.Printf("Длина в символах: %d\n", utf8.RuneCountInString(demo))

Вывод:

Строка: Go поддерживает Unicode! 🚀
Длина в байтах: 44
Длина в символах: 27

Разница: 44 байта vs 27 символов.


4. Вывод всех символов

demo := "Go поддерживает Unicode! 🚀"

fmt.Println("Символы:")
for i, char := range demo {
    fmt.Printf("Позиция %2d: '%c' (код %d)\n", i, char, char)
}

Вывод (частично):

Символы:
Позиция  0: 'G' (код 71)
Позиция  1: 'o' (код 111)
Позиция  2: ' ' (код 32)
Позиция  3: 'п' (код 1087)
Позиция  5: 'о' (код 1086)
...
Позиция 42: '🚀' (код 128640)

Обратите внимание: Позиции не последовательные (байтовые offset).


5. Ключевые концепции работы со строками

1. Строки неизменяемые (immutable)

s := "Hello"
// s[0] = 'J'  // ❌ Ошибка компиляции

Решение: Преобразование в []rune.


2. len() возвращает байты, не символы

s := "Привет"
fmt.Println(len(s))                      // 12 (байт)
fmt.Println(utf8.RuneCountInString(s))   // 6 (символов)

Важно: Для подсчёта символов используйте utf8.RuneCountInString().


3. Используйте руны для Unicode

s := "Привет"

// ❌ Неправильно
for i := 0; i < len(s); i++ {
    fmt.Printf("%c", s[i])  // Мусор
}

// ✅ Правильно
for _, r := range s {
    fmt.Printf("%c", r)  // Привет
}

for range корректно работает с Unicode.


4. Разница между byte и rune

s := "A"

b := s[0]           // byte (uint8) = 65
r := []rune(s)[0]   // rune (int32) = 65

fmt.Printf("byte: %T\n", b)  // uint8
fmt.Printf("rune: %T\n", r)  // int32

Критично: byte — 1 байт, rune — Unicode символ.


6. Сводная таблица основных операций

Операция Код Результат
Длина в байтах len(s) Количество байт
Длина в символах utf8.RuneCountInString(s) Количество символов
Доступ к байту s[i] byte
Доступ к символу []rune(s)[i] rune
Итерация по символам for _, r := range s Руны
Изменение []rune → изменение → string() Новая строка

7. Типичные ошибки

❌ Обращение по индексу к Unicode

s := "Привет"
fmt.Println(s[0])  // 208 (байт, не 'П')

❌ Использование len() для подсчёта символов

s := "Привет"
if len(s) > 10 {  // Проверяет байты!
    // ...
}

❌ Попытка изменить строку

s := "Hello"
s[0] = 'J'  // Ошибка компиляции

8. ✅ Правильные практики

Подсчёт символов

s := "Привет"
count := utf8.RuneCountInString(s)

Итерация

for _, r := range s {
    fmt.Printf("%c", r)
}

Изменение

runes := []rune(s)
runes[0] = 'X'
s = string(runes)

Проверка на пустоту

if len(s) == 0 {  // OK: быстро
    // ...
}

// Не нужно:
if utf8.RuneCountInString(s) == 0 { ... }

9. Памятка: алгоритм работы со строками

1. Есть ли Unicode?
   └─ НЕТ (только ASCII) → можно s[i]
   └─ ДА → используйте []rune или for range

2. Нужно изменить строку?
   └─ ДА → []rune → изменение → string()
   └─ НЕТ → for range для чтения

3. Нужна длина?
   └─ В байтах (размер памяти) → len(s)
   └─ В символах (для пользователя) → utf8.RuneCountInString(s)

4. Итерация?
   └─ for range s → автоматически по рунам
   └─ for i := 0; i < len(s) → только для байтов

10. Итоги

Строки иммутабельны — изменение через []rune
len(s) возвращает байты, не символы
utf8.RuneCountInString(s) — правильный подсчёт символов
for range s — корректная итерация по Unicode
[]rune(s) — для работы с отдельными символами
⚠️ Различайте byte и rune — критично для Unicode
Руны (rune) = int32 = Unicode code point
✅ Позиции в for rangeбайтовые offset, не номера символов

Ключевое правило:

ASCII    → len(), s[i], for i допустимы
Unicode  → utf8.RuneCountInString(), []rune, for range обязательны

Паттерн изменения строки:

// Шаблон для любых Unicode строк
runes := []rune(original)
// ... изменения runes[i] ...
modified := string(runes)