Регулярные выражения в Rust: мощный инструмент для работы со строками
Регулярные выражения (regex) — это незаменимый инструмент для поиска, извлечения и замены текста по заданному шаблону. В Rust работа с регулярными выражениями реализована через популярный крейт regex, который обеспечивает высокую производительность и безопасность, характерные для языка. В этой статье мы разберем основы работы с регулярными выражениями в Rust на конкретных примерах.
Установка и подключение крейта regex
Для начала работы добавьте зависимость в файл Cargo.toml вашего проекта:
[dependencies]
regex = "1"
Затем импортируйте необходимые модули в коде:
use regex::Regex;
Создание и базовое использование регулярных выражений
Основной тип для работы с регулярными выражениями — Regex. Он создается с помощью метода Regex::new(), который принимает строку с шаблоном и возвращает Result. Рассмотрим простейший пример поиска слова в строке:
use regex::Regex;
fn main() {
let re = Regex::new(r"hello").unwrap();
let text = "hello world, hello Rust!";
// Проверка наличия совпадения
if re.is_match(text) {
println!("Совпадение найдено!");
}
// Поиск первого совпадения
match re.find(text) {
Some(mat) => println!("Найдено: '{}'", mat.as_str()),
None => println!("Совпадений нет"),
}
// Поиск всех совпадений
for mat in re.find_iter(text) {
println!("Найдено: '{}' в позиции {}", mat.as_str(), mat.start());
}
}
Обратите внимание на использование сырой строки (r"..."). Это позволяет избежать экранирования обратных слешей, что особенно удобно при работе с регулярными выражениями.
Группы захвата и извлечение данных
Группы захвата позволяют извлекать части строки, соответствующие определенным частям шаблона. В Rust группы обозначаются круглыми скобками ():
use regex::Regex;
fn main() {
// Извлечение даты в формате ГГГГ-ММ-ДД
let re = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap();
let text = "Сегодня: 2024-01-15, вчера: 2024-01-14";
for cap in re.captures_iter(text) {
println!("Год: {}, Месяц: {}, День: {}",
&cap[1], &cap[2], &cap[3]);
}
// Извлечение email адресов
let email_re = Regex::new(r"([\w.]+)@([\w.]+)").unwrap();
let email_text = "Контакты: user@example.com, admin@test.org";
if let Some(caps) = email_re.captures(email_text) {
println!("Имя пользователя: {}", &caps[1]);
println!("Домен: {}", &caps[2]);
}
}
Группы можно именовать для улучшения читаемости кода:
use regex::Regex;
fn main() {
let re = Regex::new(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})").unwrap();
let text = "2024-01-15";
if let Some(caps) = re.captures(text) {
println!("Год: {}", caps.name("year").unwrap().as_str());
println!("Месяц: {}", caps.name("month").unwrap().as_str());
println!("День: {}", caps.name("day").unwrap().as_str());
}
}
Замена текста с помощью регулярных выражений
Крейт regex предоставляет мощные методы для замены текста. Рассмотрим несколько вариантов:
use regex::Regex;
fn main() {
let re = Regex::new(r"\d+").unwrap();
let text = "У меня 2 кота и 3 собаки";
// Простая замена всех цифр на слово
let result = re.replace_all(text, "несколько");
println!("{}", result); // "У меня несколько кота и несколько собаки"
// Замена с использованием групп захвата
let re2 = Regex::new(r"(\w+)@(\w+)").unwrap();
let email = "user@example.com";
let replaced = re2.replace(email, "$2@$1");
println!("{}", replaced); // "example@user.com"
// Замена с помощью замыкания
let re3 = Regex::new(r"\b(\d{1,3})\b").unwrap();
let numbers = "Числа: 5, 42, 100, 1000";
let squared = re3.replace_all(numbers, |caps: ®ex::Captures| {
let num: i32 = caps[1].parse().unwrap();
(num * num).to_string()
});
println!("{}", squared); // "Числа: 25, 1764, 10000, 1000"
}
Практические примеры и валидация данных
Регулярные выражения часто используются для валидации пользовательского ввода. Рассмотрим несколько распространенных сценариев:
use regex::Regex;
fn validate_email(email: &str) -> bool {
let re = Regex::new(r"^[\w.+-]+@[\w-]+\.[\w.]+$").unwrap();
re.is_match(email)
}
fn validate_phone(phone: &str) -> bool {
// Российский номер телефона: +7 (XXX) XXX-XX-XX
let re = Regex::new(r"^\+7\s?\(\d{3}\)\s?\d{3}-\d{2}-\d{2}$").unwrap();
re.is_match(phone)
}
fn extract_urls(text: &str) -> Vec<String> {
let re = Regex::new(r"https?://[\w./-]+").unwrap();
re.find_iter(text)
.map(|mat| mat.as_str().to_string())
.collect()
}
fn main() {
// Валидация
println!("Email валиден: {}", validate_email("user@example.com"));
println!("Телефон валиден: {}", validate_phone("+7 (999) 123-45-67"));
// Извлечение URL
let text = "Посетите https://rust-lang.org и http://example.com";
let urls = extract_urls(text);
println!("Найденные URL: {:?}", urls);
// Разделение строки с сохранением разделителей
let re = Regex::new(r"([.,!?])").unwrap();
let sentence = "Привет, мир! Как дела?";
let parts: Vec<&str> = re.split(sentence).collect();
println!("Части предложения: {:?}", parts);
}
Работа с Unicode и флагами
Крейт regex поддерживает Unicode по умолчанию. Однако вы можете управлять его поведением с