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

Как посчитать количество символов в строке?

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

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

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

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

Подсчёт количества символов в строке в Go

В языке Go подсчёт символов в строке — не такая тривиальная задача, как может показаться на первый взгляд, из-за особенностей кодировки UTF-8 и различия между байтами, рунами (кодовыми точками Unicode) и графемными кластерами. Рассмотрим основные подходы.

1. Длина строки в байтах (неправильно для Unicode)

Стандартная функция len() возвращает количество байтов, а не символов:

str := "Привет, мир!"
byteCount := len(str)
fmt.Println(byteCount) // 20, а не 12 символов!

Для ASCII-строк это работает, но для Unicode-строк (кириллица, эмодзи и т.д.) результат будет некорректным, так как русские буквы кодируются 2 байтами в UTF-8.

2. Количество рун (кодовых точек) - правильный базовый подход

Для подсчёта символов в Unicode-строках используйте преобразование в срез рун или функцию utf8.RuneCountInString():

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    str := "Привет, мир!"
    
    // Способ 1: через преобразование в срез рун
    runeCount1 := len([]rune(str))
    fmt.Println(runeCount1) // 12
    
    // Способ 2: используя пакет unicode/utf8
    runeCount2 := utf8.RuneCountInString(str)
    fmt.Println(runeCount2) // 12
    
    // Способ 3: итерация по строке
    runeCount3 := 0
    for range str {
        runeCount3++
    }
    fmt.Println(runeCount3) // 12
}

Руна (rune) в Go — это псевдоним для int32, представляющий кодовую точку Unicode. Этот подход корректно работает для большинства случаев, но имеет важное ограничение.

3. Важное ограничение: графемные кластеры

Unicode содержит составные символы, которые визуально выглядят как один символ, но состоят из нескольких кодовых точек:

  • Эмодзи с модификаторами кожи: 👨🏿 (2 руны)
  • Буквы с диакритическими знаками: "é" (2 руны: 'e' + combining acute accent)
  • Флаги: 🇷🇺 (2 руны региональных индикаторов)
package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    emoji := "👨‍👩‍👧‍👦" // Семья: 4 взрослых + 2 детей
    fmt.Println("Байты:", len(emoji)) // 25
    fmt.Println("Руны:", utf8.RuneCountInString(emoji)) // 11
    fmt.Println("Видимых символов:", 1) // Визуально 1 эмодзи!
}

4. Правильный подсчёт графемных кластеров

Для корректного подсчёта видимых символов используйте пакет golang.org/x/text/unicode/norm или github.com/rivo/uniseg:

package main

import (
    "fmt"
    "unicode/utf8"
    "golang.org/x/text/unicode/norm"
)

// Подсчёт графемных кластеров с помощью пакета norm
func countGraphemes(s string) int {
    iter := norm.Iter{}
    iter.InitString(norm.NFC, s)
    count := 0
    for !iter.Done() {
        iter.Next()
        count++
    }
    return count
}

func main() {
    str := "Café" // Составной символ: 'e' + acute accent
    fmt.Println("Байты:", len(str)) // 5
    fmt.Println("Руны:", utf8.RuneCountInString(str)) // 5
    fmt.Println("Графемы:", countGraphemes(str)) // 4
}

Более современный и удобный вариант — библиотека uniseg:

import "github.com/rivo/uniseg"

func main() {
    str := "👨‍👩‍👧‍👦 Hello!"
    gr := uniseg.NewGraphemes(str)
    count := 0
    for gr.Next() {
        count++
    }
    fmt.Println("Графемные кластеры:", count) // 8
}

5. Практические рекомендации

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

  1. len(string) — только для ASCII-строк или когда нужен именно размер в байтах (например, для сетевых протоколов)
  2. len([]rune(str)) или utf8.RuneCountInString() — в большинстве случаев для текста на естественных языках
  3. Специализированные библиотеки (uniseg) — когда важна точность отображения (UI, соцсети, аналитика текста)

Производительность:

  • len(string) — O(1), моментально
  • len([]rune(str)) — O(n), выделяет память под срез
  • utf8.RuneCountInString() — O(n), не выделяет память
  • Подсчёт графемных кластеров — O(n), наиболее ресурсоёмкий

Вывод

В Go нет единого решения для подсчёта символов. Выбор метода зависит от:

  • Типа строки (ASCII/Unicode)
  • Требований к точности (руны vs графемы)
  • Соображений производительности

Для большинства практических задач с текстом на естественных языках utf8.RuneCountInString() является оптимальным выбором, балансируя между корректностью и производительностью. Для интерфейсов пользователя с поддержкой эмодзи и сложных Unicode-символов используйте специализированные библиотеки для работы с графемными кластерами.

Как посчитать количество символов в строке? | PrepBro