Задачи про руны

Открой мир рун: гадания, толкования и магические практики для тех, кто ищет ответы

Руны – термин‚ который чаще всего встречается в программировании на языке Go. В Go руна представляет собой 32‑битный целый тип rune‚ который хранит один Unicode‑символ. Понимание того‚ как работать с рунами‚ критически важно при решении задач‚ связанных с обработкой текста‚ локализацией и анализом символов.

Содержание

  1. Что такое руны?
  2. Базовые операции с рунами
  3. Типичные задачи про руны
  4. Продвинутые задачи
  5. Полезные советы и ловушки

Что такое руны?

В Go тип rune – это алиас для int32. Он используется для представления одного Unicode‑кода точки (code point). Это отличает руны от обычных байтов (byte)‚ которые хранят только 8‑битные значения и подходят лишь для ASCII‑символов.

Пример:

var r rune = 'Ж' // код точки U+0416
fmt.Printf("%U
"‚ r) // выводит U+0416

Почему руны важны?

  • Позволяют корректно обрабатывать многоязычный текст (русский‚ китайский‚ эмодзи и т.д.).
  • Обеспечивают правильный подсчёт символов‚ а не байтов.
  • Упрощают работу с регистром‚ классификацией символов и их нормализацией.

Базовые операции с рунами

Преобразование строки в срез рун

s := "Привет‚ мир!"
runes := []rune(s) // каждый элемент – отдельный Unicode‑символ
fmt.Println(len(runes)) // количество символов‚ а не байтов

Обратное преобразование

newStr := string(runes)
fmt.Println(newStr) // "Привет‚ мир!"

Получение кода точки

for i‚ r := range s {
 fmt.Printf("позиция %d: %c (U+%04X)
"‚ i‚ r‚ r)
}

Типичные задачи про руны

Задача 1. Подсчёт количества гласных в строке

Требуется посчитать‚ сколько гласных букв (русских) содержится в тексте.

func countVowels(s string) int {
 vowels := map[rune]bool{
 'а': true‚ 'е': true‚ 'ё': true‚ 'и': true‚
 'о': true‚ 'у': true‚ 'ы': true‚ 'э': true‚
 'ю': true‚ 'я': true‚
 'А': true‚ 'Е': true‚ 'Ё': true‚ 'И': true‚
 'О': true‚ 'У': true‚ 'Ы': true‚ 'Э': true‚
 'Ю': true‚ 'Я': true‚
 }
 cnt := 0
 for _‚ r := range s {
 if vowels[r] {
 cnt++
 }
 }
 return cnt
}

Задача 2. Проверка‚ является ли строка палиндромом (учитывая Unicode)

func isPalindrome(s string) bool {
 // Убираем пробелы и знаки препинания‚ переводим в нижний регистр
 var cleaned []rune
 for _‚ r := range []rune(strings.ToLower(s)) {
 if unicode.IsLetter(r) || unicode.IsNumber(r) {
 cleaned = append(cleaned‚ r)
 }
 }
 // Сравниваем слева и справа
 for i‚ j := 0‚ len(cleaned)-1; i < j; i‚ j = i+1‚ j-1 {
 if cleaned[i] != cleaned[j] {
 return false
 }
 } return true
}

Задача 3. Сортировка строки по Unicode‑кодам

func sortRunes(s string) string {
 r := []rune(s)
 sort.Slice(r‚ func(i‚ j int) bool { return r[i] < r[j] })
 return string(r)
}

Задача 4. Замена всех эмодзи на знак «#»

Эмодзи находятся в нескольких диапазонах Unicode. Мы будем использовать unicode.Is и предопределённые диапазоны.

func replaceEmoji(s string) string {
 var b strings.Builder
 for _‚ r := range s {
 if unicode.Is(unicode.Emoticons‚ r) ||
 unicode.Is(unicode.Symbols‚ r) && (r >= 0x1F300 && r <= 0x1F5FF) ||
 unicode.Is(unicode.Symbols‚ r) && (r >= 0x1F600 && r <= 0x1F64F) {
 b.WriteRune('#')
 } else {
 b.WriteRune(r)
 }
 }
 return b.String
}

Продвинутые задачи

Задача 5. Нормализация Unicode (NFC/NFD) и сравнение строк

В Unicode один и тот же визуальный символ может иметь несколько представлений. Для корректного сравнения нужно привести строки к одной форме.

import "golang.org/x/text/unicode/norm"
func equalNormalized(a‚ b string) bool {
 na := norm.NFC.String(a)
 nb := norm.NFC.String(b)
 return na == nb
}

Задача 6. Выделение всех уникальных рунических символов в тексте

func uniqueRunes(s string) []rune {
 seen := make(map[rune]struct{})
 var result []rune
 for _‚ r := range s {
 if _‚ ok := seen[r]; !ok {
 seen[r] = struct{}{}
 result = append(result‚ r)
 }
 }
 return result
}

Задача 7. Подсчёт частоты каждого символа (гистограмма)

func histogram(s string) map[rune]int {
 freq := make(map[rune]int)
 for _‚ r := range s {
 freq[r]++
 }
 return freq
}

Полезные советы и ловушки

  • Не путайте байты и руны. Операция len(s) возвращает количество байтов‚ а не символов. Для подсчёта символов используйте len([]rune(s)) или цикл for range.
  • Учтите комбинирующие знаки. Некоторые символы (например‚ буква «а» + диакритический знак) являются двумя рунами‚ но визуально выглядят как один символ. Нормализация помогает решить эту проблему.
  • Проверка на принадлежность к категории Unicode. Пакет unicode предоставляет функции unicode.IsLetter‚ unicode.IsNumber‚ unicode.IsSpace и т.д.‚ которые работают корректно с любой кодовой точкой.
  • Эффективность. При частом добавлении/удалении символов лучше использовать bytes.Buffer или strings.Builder‚ а не конкатенацию строк.
  • Работа с UTF‑8 в файлах. При чтении файлов убедитесь‚ что у вас установлен правильный декодер (обычно bufio.NewReader + ReadString('
    ')
    уже возвращает UTF‑8).

Руны – фундаментальная часть работы с текстом в Go. Понимание их особенностей позволяет решать как простые‚ так и сложные задачи: от подсчёта гласных до нормализации Unicode и обработки эмодзи. В статье мы рассмотрели основные операции‚ типовые задачи и продемонстрировали готовые решения‚ которые можно сразу использовать в своих проектах.

Не забывайте про тестирование — проверяйте свои функции на разных наборах символов (ASCII‚ кириллица‚ азиатские иероглифы‚ эмодзи). Это поможет избежать скрытых багов‚ связанных с особенностями Unicode.

Если у вас возникнут вопросы или вы захотите поделиться своими решениями‚ оставляйте комментарии ниже. Удачной кодировки!