Изменение строк и итоговые концепции
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)