Руны – термин‚ который чаще всего встречается в программировании на языке Go. В Go руна представляет собой 32‑битный целый тип rune‚ который хранит один Unicode‑символ. Понимание того‚ как работать с рунами‚ критически важно при решении задач‚ связанных с обработкой текста‚ локализацией и анализом символов.
Содержание
- Что такое руны?
- Базовые операции с рунами
- Типичные задачи про руны
- Продвинутые задачи
- Полезные советы и ловушки
Что такое руны?
В 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.
Если у вас возникнут вопросы или вы захотите поделиться своими решениями‚ оставляйте комментарии ниже. Удачной кодировки!