Длина строки: байты vs символы

1. Критическая особенность Go

Строка в Go = последовательность байт, не символов.

s := "hello"
fmt.Println(len(s))  // 5 — количество БАЙТ, не символов

Функция len() возвращает длину в байтах.


2. ASCII строки (1 символ = 1 байт)

ascii := "hello"

fmt.Printf("Строка: %s\n", ascii)
fmt.Printf("len(): %d байт\n", len(ascii))

Вывод:

Строка: hello
len(): 5 байт

Совпадает: 5 символов = 5 байт.


3. Кириллица (1 символ = 2 байта)

cyrillic := "привет"

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

Вывод:

Строка: привет
len(): 12 байт
Символов: 6

Расхождение: 6 символов, но 12 байт (6 × 2).


4. Эмодзи (1 символ = 4 байта)

emoji := "😀"

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

Вывод:

Строка: 😀
len(): 4 байт
Символов: 1

1 эмодзи = 4 байта в UTF-8.


5. Таблица размеров символов

Тип символа Пример Байт на символ len() Символов
ASCII "hello" 1 5 5
Кириллица "привет" 2 12 6
Яблоко "🍎" 3 3 1
Эмодзи "😀" 4 4 1
Иероглифы "你好" 3 6 2

Вывод: Размер символа варьируется от 1 до 4 байт.


6. Правильный подсчёт символов

❌ Неправильно: len()

s := "привет"
fmt.Println(len(s))  // 12 — это БАЙТЫ, не символы!

✅ Правильно: utf8.RuneCountInString()

s := "привет"
count := utf8.RuneCountInString(s)
fmt.Println(count)  // 6 — правильное количество символов

Работает корректно для любых Unicode-символов.


7. Примеры с разными кодировками

Смешанная строка

mixed := "Hello мир 😀"

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

Вывод:

Строка: Hello мир 😀
len(): 19 байт
Символов: 12

Разбор:

  • "Hello " → 6 байт (6 ASCII)
  • "мир" → 6 байт (3 × 2)
  • " " → 1 байт
  • "😀" → 4 байта
  • Итого: 17 байт, 11 символов

Китайские иероглифы

chinese := "你好"  // "Привет" по-китайски

fmt.Printf("len(): %d байт\n", len(chinese))
fmt.Printf("Символов: %d\n", utf8.RuneCountInString(chinese))

Вывод:

len(): 6 байт
Символов: 2

1 иероглиф = 3 байта.


8. Практический пример

text := "🍎🍊🍋"  // 3 фрукта

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

Вывод:

Строка: 🍎🍊🍋
len(): 12 байт
Символов: 3

Каждый фрукт = 4 байта.


9. Типичная ошибка

s := "привет"

// ❌ ОШИБКА: попытка обратиться к "символу"
for i := 0; i < len(s); i++ {
    fmt.Printf("%c ", s[i])  // Выведет мусор!
}

Вывод: Байты UTF-8, не символы.


✅ Правильно: итерация по рунам

s := "привет"

for _, r := range s {
    fmt.Printf("%c ", r)  // п р и в е т
}

for range по строке итерирует по рунам, не байтам.


10. Сравнение функций

Функция Что возвращает Применение
len(s) Длина в байтах Размер в памяти, работа с байтами
utf8.RuneCountInString(s) Количество символов Подсчёт видимых символов
for range s Итерация по рунам Обход символов

11. Когда использовать что

len() — для байтов

// Проверка пустоты (быстро)
if len(s) == 0 {
    fmt.Println("Пустая строка")
}

// Выделение памяти
buf := make([]byte, len(s))

utf8.RuneCountInString() — для символов

// Ограничение длины для пользователя
if utf8.RuneCountInString(username) > 20 {
    fmt.Println("Имя слишком длинное")
}

// Подсчёт символов в тексте
count := utf8.RuneCountInString(message)
fmt.Printf("Введено символов: %d\n", count)

12. Итоги

len(s) возвращает длину в байтах, не символах ✅ ASCII — 1 байт на символ ✅ Кириллица — 2 байта на символ ✅ Некоторые символы (🍎) — 3 байта ✅ Эмодзи — 4 байта на символ ✅ utf8.RuneCountInString(s) — правильный подсчёт символов ✅ for range итерирует по рунам, не байтам ⚠️ Будьте внимательны при работе с разными кодировками

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

len(s)                      → байты (размер в памяти)
utf8.RuneCountInString(s)   → символы (видимые знаки)
for range s                 → итерация по символам

Практический совет:

// ❌ Неправильно для Unicode
if len(name) > 10 { ... }

// ✅ Правильно
if utf8.RuneCountInString(name) > 10 { ... }