Работа с Unicode-символами в Go требует особого подхода. В этой статье разберёмся, как правильно выбирать случайные символы из слайса рун, почему важно использовать именно руны вместо байтов, и научимся создавать надёжные генераторы случайных строк.
Почему именно руны?
Разница между байтами и рунами
В Go строки представляют собой байтовые срезы, но это создаёт проблемы при работе с Unicode-символами:
- Байты (uint8) ⎻ работают только с ASCII
- Руны (int32) ⎻ поддерживают все Unicode символы, включая русские буквы, иероглифы и эмодзи
Базовый пример: случайный выбор руны
package main
import (
«fmt»
«math/rand»
«time»
)
func main {
// Инициализируем генератор случайных чисел
rand.Seed(time.Now.UnixNano)
// Слайс рун с различными символами
chars := []rune{‘A’, ‘Б’, ‘Г’, ‘Д’, », ‘🔥’, ‘1’, ‘Å’, ‘ä’, ‘ö’}
// Выбираем случайную руну
randomRune := chars[rand.Intn(len(chars))]
fmt.Printf(«Случайная руна: %c (код: U+%04X)
«, randomRune, randomRune)
}
Создание надёжного генератора случайных строк
Шаг 1: Формирование алфавита
Для генерации безопасных паролей используем расширенный набор символов:
alphabet := []rune(«ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ» +
«abcdefghijklmnopqrstuvwxyzåäö» +
«0123456789!@#$%^&*»)
Шаг 2: Функция генерации строки
func generateRandomString(length int) string {
rand.Seed(time.Now.UnixNano)
chars := []rune(«ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ» +
«abcdefghijklmnopqrstuvwxyzåäö» +
«0123456789»)
var b strings.Builder
for i := 0; i < length; i++ { b.WriteRune(chars[rand.Intn(len(chars))]) } return b.String }
Продвинутые техники
Криптографически стойкая генерация
Для критически важных задач используйте crypto/rand вместо math/rand:
import «crypto/rand»
func secureRandomRune(chars []rune) (rune, error) {
var n uint32
err := binary.Read(rand.Reader, binary.LittleEndian, &n)
if err != nil {
return 0, err
}
return chars[int(n)%len(chars)], nil
}
Работа с последним элементом
В Go нет отрицательной индексации, поэтому последний элемент получаем так:
lastRune := slice[len(slice)-1]
Практический пример: генератор паролей
package main
import (
«fmt»
«math/rand»
«strings»
«time»
)
type PasswordGenerator struct {
alphabet []rune
}
func NewPasswordGenerator PasswordGenerator {
return &PasswordGenerator{
alphabet: []rune(«ABCDEFGHIJKLMNOPQRSTUVWXYZ» +
«abcdefghijklmnopqrstuvwxyz» +
«0123456789» +
«!@#$%^&_+-=[]{}|;:,.<>?»),
}
}
func (pg *PasswordGenerator) Generate(length int) string {
rand.Seed(time.Now.UnixNano)
var builder strings.Builder
for i := 0; i < length; i++ { idx := rand.Intn(len(pg.alphabet)) builder.WriteRune(pg.alphabet[idx]) } return builder.String } func main { generator := NewPasswordGenerator passwords := []string{ generator.Generate(8), generator;Generate(12), generator.Generate(16), } fmt.Println("
Сгенерированные пароли:
«)
for i, pwd := range passwords {
fmt.Printf(«
Пароль %d: %s
«, i+1, pwd)
}
}
Распространённые ошибки и решения
Ошибка 1: Использование байтов вместо рун
❌ Неправильно:
// Это даст ошибку с Unicode символами
chars := []byte(«Привет»)
✅ Правильно:
chars := []rune(«Привет»)
Ошибка 2: Повторная инициализация генератора
Инициализируйте rand.Seed только один раз при запуске программы:
func init {
rand.Seed(time.Now.UnixNano)
}
Работа с рунами в Go ⎻ это ключ к корректной обработке Unicode-текста. Используйте:
- []rune для хранения символов
- rand.Intn(len(slice)) для случайного выбора
- crypto/rand для критически важных задач
- strings.Builder для эффективной конкатенации
Следуя этим рекомендациям, вы создадите надёжные и эффективные генераторы случайных строк в ваших Go-приложениях.