← Назад к вопросам

Что будет, если обратиться к части строки по индексу?

1.0 Junior🔥 241 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Обращение к части строки по индексу в Go

В языке Go строка (string) является неизменяемой (immutable) последовательностью байтов, представляющей собой UTF-8 кодированный текст. При попытке обращения к части строки по индексу происходят несколько важных вещей, которые нужно понимать.

Как работает индексация строк

При обращении к строке по индексу (например, s[i]) вы получаете байт по указанной позиции, а не символ (руну):

s := "Привет, мир!"
b := s[0]  // b имеет тип byte, значение 208 (первый байт UTF-8 кодировки 'П')

Код выше демон andстрирует ключевую особенность: строка индексируется по байтам, а не по символам. Это важно, потому что в UTF-8 символы могут занимать от 1 до 4 байтов.

Пример с многобайтовыми символами

package main

import "fmt"

func main() {
    s := "Go世界"  // "Go" + китайские иероглифы
    
    fmt.Println("Длина строки в байтах:", len(s))      // 8 байт
    fmt.Println("Срез байтов:", []byte(s))            // [71 111 228 184 150 231 149 140]
    
    // Доступ по индексу возвращает отдельные байты
    fmt.Println("s[0]:", s[0])     // 71 (байт 'G')
    fmt.Println("s[2]:", s[2])     // 228 (первый байт иероглифа '世')
    fmt.Printf("s[2]: %c\n", s[2]) // некорректный вывод (только часть символа)
    
    // Правильный способ работы с символами
    for i, r := range s {
        fmt.Printf("Позиция %d: руна %c (код %U)\n", i, r, r)
    }
}

Что происходит при обращении к индексу

  1. Возвращается значение типа byte (псевдоним uint8)
  2. Нет проверки границ на этапе компиляции - это происходит во время выполнения
  3. Паника при выходе за границы:
    s := "Hello"
    b := s[10]  // panic: runtime error: index out of range [10] with length 5
    

Работа с подстроками и срезами строк

Для получения части строки используется срез (slicing):

s := "Hello, World!"
sub := s[7:12]  // "World"

Важные особенности срезов строк:

  • Создается новая строка (строка неизменяема)
  • Не происходит копирования данных - новая строка ссылается на тот же массив байтов
  • Синтаксис s[i:j] включает байт i и исключает байт j

Проблемы с многобайтовыми символами

s := "Привет"
// Проблемный код - может разорвать многобайтовый символ
broken := s[0:4]  // Может содержать некорректный UTF-8
fmt.Println(broken)  // Может вывести "" или другую белиберду

// Правильный подход для работы с рунами
runes := []rune(s)
if len(runes) > 2 {
    firstTwoChars := string(runes[0:2])  // "Пр"
}

Рекомендации по работе со строками

Для корректной работы с текстом:

  1. Используйте for range для итерации по символам:

    for index, runeValue := range s {
        // index - позиция в байтах
        // runeValue - код символа (тип rune)
    }
    
  2. Преобразуйте в []rune для произвольного доступа к символам:

    runes := []rune(s)
    thirdChar := runes[2]  // Третий символ
    
  3. Используйте пакет unicode/utf8 для низкоуровневых операций:

    import "unicode/utf8"
    
    r, size := utf8.DecodeRuneInString(s[i:])  // Декодирование руны
    

Производительность и память

  • Обращение по индексу s[i] - O(1) операция
  • Преобразование в []rune - O(n) по времени и памяти
  • Создание подстроки s[i:j] - O(1) по времени, не копирует данные

Заключение

Обращение к части строки по индексу в Go возвращает байт, а не символ, что является источником частых ошибок при работе с Unicode. Для корректной обработки текста необходимо использовать итерацию с помощью for range, преобразование в []rune или функции из пакета unicode/utf8. Понимание этого различия между байтами и символами (рунами) критически важно для написания корректных программ, работающих с интернациональным текстом.