Перейти к основному содержимому

Строки, руны и Unicode в Go

Разработчику

См. также: Типы данных и переменные · Справочник.

Модель строки

Тип stringнеизменяемая последовательность байтов в кодировке UTF-8. Это не массив «символов»:

  • len(s) — число байт, не рун;
  • индекс s[i] — один байт (byte, алиас uint8), не символ;
  • срез s[0:2] может разрезать многобайтовый символ пополам и дать невалидный UTF-8.

Тип rune — алиас int32 для кодовой точки Unicode (U+XXXX). Литерал: 'Ж', '🙂'.


Итерация по символам

s := "café"
for i, r := range s {
fmt.Printf("байт %d: U+%04X %c\n", i, r, r)
}

range по строке декодирует UTF-8 и отдаёт (индекс байта начала руны, rune). Количество итераций — число рун, не len(s).

Подсчёт рун без аллокации:

n := utf8.RuneCountInString(s)

Пакет unicode/utf8: utf8.DecodeRuneInString, utf8.ValidString.


Сборка и изменение текста

Строки неизменяемы — конкатенация + в цикле создаёт много копий. Для накопления:

var b strings.Builder
b.Grow(estimatedSize) // опционально
b.WriteString("prefix")
b.WriteRune('—')
result := b.String()

strings.Builder не копирует буфер при каждом добавлении (в отличие от []byte + append без предварительного Grow в горячих циклах).

Преобразования:

НужноКод
string[]rune[]rune(s) — копия всех рун
[]runestringstring(runes)
string[]byte[]byte(s) — копия байтов
Без копии байтов (осторожно)unsafe или работа с []byte изначально

Пакет strings

Часто используемые функции:

  • Contains, HasPrefix, HasSuffix, TrimSpace, TrimPrefix
  • Split, Join, Replace, ReplaceAll
  • Compare — лексикографическое сравнение байтов (не локаль!)
  • EqualFold — регистронезависимое сравнение для ASCII и простых случаев Unicode

Поиск подстроки: strings.Index, strings.Count. Для множественных разделителей — strings.FieldsFunc.


Сравнение и нормализация

Два визуально одинаковых текста могут состоять из разных последовательностей кодовых точек (например, буква «é» как один символ U+00E9 или как «e» + комбинирующий акут). Для надёжного сравнения имён, поиска, ключей:

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

a := norm.NFC.String(inputA)
b := norm.NFC.String(inputB)
if a == b { /* ... */ }

Пакет golang.org/x/text — вне стандартной библиотеки, но де-факто стандарт для i18n. Для чистого ASCII достаточно == или EqualFold.


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

  1. Срез по байтамs[:3] для обрезки «трёх символов» кириллицы.
  2. Индексация в цикле for i := 0; i < len(s); i++ — обход байтов, не символов (допустимо для ASCII-протоколов).
  3. Путать len и количество символов в UI и лимитах полей.
  4. Регулярные выражения — в Go regexp работает с индексами байт; для Unicode-символов иногда нужны классы \p{L}.

Практические сценарии

СценарийПодход
Лог, протокол ASCII[]byte, len, индексы байт
Пользовательский ввод, UIrange, utf8, нормализация
JSON APIstring как UTF-8; валидировать на границе
Хеш или HMAC[]byte исходного UTF-8, не «нормализованная» строка без правила

Теги json:"name" и сериализация — в работе с БД и структурами и интерфейсах.


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).