Перейти к основному содержимому
Модуль 4 · Урок 4 из 519/50 в курсе~12 min
Уроки модуля (4/5)

Строки, байты и руны

В Go string — это неизменяемая последовательность байтов. Строковые литералы кодируются в UTF-8. rune — это псевдоним int32 и представляет одну кодовую точку Юникода.

Понимание разницы между байтами и рунами необходимо для избегайте ошибок, когда строка содержит акценты, смайлы, идеограммы или любые другие символ, отличный от ASCII.

string = байты, а не символы

Go
s := "ciaò"
fmt.Println(len(s))   // 5 — not 4!
fmt.Println(s[0])     // 99 ('c' as a byte)
fmt.Println(s[3])     // 195 (first byte of 'ò' in UTF-8)

len(s) возвращает количество байт, а не видимых символов. Индексация s[i] возвращает i-й байт (uint8), а не символ.

for range повторяет руны

for i, r := range s декодирует UTF-8 на лету: i — это смещение байтов начала руны r — это руна (int32):

Go
s := "ciaò"
for i, r := range s {
    fmt.Printf("%d %c (U+%04X)\n", i, r, r)
}
// 0 c (U+0063)
// 1 i (U+0069)
// 2 a (U+0061)
// 3 ò (U+00F2)   <- starts at byte 3, takes 2 bytes

[]rune(s): посимвольное индексирование

Явное преобразование: выделяет новый фрагмент рун. Дорого, но иногда необходимо:

Go
runes := []rune("ciaò")
fmt.Println(len(runes))    // 4
fmt.Println(string(runes[3])) // ò

string(runes) — это обратное преобразование: принимает []rune и создает строка UTF-8.

Строки неизменяемы

Go
s := "ciao"
// s[0] = 'C'   // ERROR: cannot assign to s[0]

Для мутации конвертируйте в []byte или []rune, действуйте, конвертируйте обратно:

Go
b := []byte(s)
b[0] = 'C'
s = string(b)   // "Ciao"

Конкатенация

Оператор + работает, но для многих конкатенаций в цикле используйте strings.Builder (более эффективный, позволяет избежать промежуточных выделений):

Go
var sb strings.Builder
for i := 0; i < 5; i++ {
    sb.WriteString("ab")
}
fmt.Println(sb.String())   // "ababababab"

Полезные пакеты

  • strings: Contains, HasPrefix, Split, Join, ToUpper, Replace...
  • strconv: Atoi, Itoa, ParseFloat, FormatInt...
  • unicode: IsLetter, IsDigit, ToUpper...
  • unicode/utf8: RuneCountInString (альтернатива len([]rune(s))).

Попробуйте это

Упражнение#go.m4.l4.e1
Попыток: 0Загрузка…

Повторите строку s с диапазоном и напечатайте индекс и руну, используя %d %c.

Загрузка редактора…
Показать подсказку

диапазон по строке возвращает смещение байтов + руну.

Решение доступно после 3 попыток

Упражнение#go.m4.l4.e2
Попыток: 0Загрузка…

Преобразуйте s в []rune и выведите его длину в символах.

Загрузка редактора…
Показать подсказку

Явное преобразование: `[]rune(s)`.

Решение доступно после 3 попыток

Quiz#go.m4.l4.e3
Готово

Что такое len("ciaò")?

Go
s := "ciaò"
fmt.Println(len(s))
Варианты ответа

Резюме

  • string = неизменяемая последовательность байтов в кодировке UTF-8.
  • len(s) = байты; s[i] = i-й байт (uint8).
  • for i, r := range s расшифровывает руны; i — это смещение байтов.
  • []rune(s) для индексации по символам (затратно, выделяет).
  • Чтобы изменить: пройдите []byte или []rune.
  • Для объединения в цикле: strings.Builder.
  • Пакеты ключей: strings, strconv, unicode, unicode/utf8.