Регулярные выражения в Go: мощный инструмент для работы с текстом
Регулярные выражения (regex) — незаменимый инструмент для поиска, извлечения и замены текста по шаблону. В языке Go (Golang) работа с регулярными выражениями реализована через стандартный пакет regexp. Он предоставляет быстрый и безопасный способ работы с шаблонами, используя синтаксис, близкий к Perl и RE2. В этой статье мы разберём основные функции пакета на практических примерах.
1. Основы работы с пакетом regexp
Пакет regexp следует принципу «компилируй один раз, используй много раз». Это повышает производительность при многократном применении одного шаблона. Основные функции:
regexp.Compile(expr)— компилирует регулярное выражение. Возвращает*Regexpи ошибку.regexp.MustCompile(expr)— то же самое, но вызывает панику при ошибке. Удобно для инициализации глобальных переменных.regexp.MatchString(expr, s)— проверяет, содержит ли строкаsхотя бы одно совпадение с шаблоном.
package main
import (
"fmt"
"regexp"
)
func main() {
// Простая проверка: содержит ли строка цифры?
matched, _ := regexp.MatchString(`\d+`, "Заказ №12345")
fmt.Println(matched) // true
// Компилируем шаблон для многократного использования
re := regexp.MustCompile(`[a-z]+`)
fmt.Println(re.MatchString("hello")) // true
fmt.Println(re.MatchString("HELLO")) // false
}
Обратите внимание: по умолчанию MatchString ищет частичное совпадение (как strings.Contains). Для полного совпадения строки с шаблоном используйте якоря ^ и $.
2. Поиск и извлечение данных
Для поиска всех совпадений или первого вхождения используйте методы FindString, FindAllString, FindStringSubmatch и их аналоги для срезов байтов.
2.1. Поиск первого совпадения
re := regexp.MustCompile(`\b\w+@\w+\.\w+\b`)
email := re.FindString("Свяжитесь с нами: support@example.com или info@test.org")
fmt.Println(email) // support@example.com
2.2. Поиск всех совпадений
re := regexp.MustCompile(`\d{4}`)
numbers := re.FindAllString("Коды: 1234, 5678, 9012", -1)
fmt.Println(numbers) // [1234 5678 9012]
Второй аргумент FindAllString — максимальное количество совпадений. -1 означает «все».
2.3. Группы захвата (submatch)
Группы захвата позволяют извлечь части совпадения. Они задаются круглыми скобками ().
re := regexp.MustCompile(`(\w+)@(\w+\.\w+)`)
match := re.FindStringSubmatch("user@example.com")
fmt.Println(match) // [user@example.com user example.com]
// match[0] — полное совпадение
// match[1] — первая группа (user)
// match[2] — вторая группа (example.com)
3. Замена текста с помощью регулярных выражений
Пакет regexp предоставляет два основных метода для замены:
ReplaceAllString(src, repl)— заменяет все совпадения на строкуrepl.ReplaceAllStringFunc(src, replFunc)— заменяет каждое совпадение, используя функцию обратного вызова.
3.1. Простая замена
re := regexp.MustCompile(`\s+`)
result := re.ReplaceAllString("Много пробелов здесь", " ")
fmt.Println(result) // "Много пробелов здесь"
3.2. Замена с использованием групп захвата
В строке замены можно ссылаться на группы через $1, $2 и т.д.
re := regexp.MustCompile(`(\w+)\s+(\w+)`)
result := re.ReplaceAllString("Иван Петров", "$2, $1")
fmt.Println(result) // "Петров, Иван"
3.3. Замена с функцией
re := regexp.MustCompile(`\d+`)
result := re.ReplaceAllStringFunc("Цена: 100 рублей", func(s string) string {
// Увеличиваем каждое число на 10
num, _ := strconv.Atoi(s)
return strconv.Itoa(num + 10)
})
fmt.Println(result) // "Цена: 110 рублей"
4. Флаги и расширенные возможности
В Go нет встроенного синтаксиса флагов (как /i в Perl). Вместо этого флаги задаются в начале шаблона с помощью специальных конструкций:
- (?i) — регистронезависимый режим.
- (?m) — многострочный режим (якоря
^и$работают для каждой строки). - (?s) — режим, при котором
.совпадает с любым символом, включая перевод строки.
4.1. Регистронезависимый поиск
re := regexp.MustCompile(`(?i)go`)
fmt.Println(re.FindString("Go is great")) // "Go"
fmt.Println(re.FindString("I love GO")) // "GO"
4.2. Многострочный режим
text := `строка1
строка2
строка3`
// Без флага ^ ищет только в начале всей строки
re1 := regexp.MustCompile(`^строка`)
fmt.Println(re1.FindAllString(text, -1)) // [строка]
// С флагом (?m) ^ работает для каждой строки
re2 := regexp.MustCompile(`(?m)^строка`)
fmt.Println(re2.FindAllString(text, -1)) //