Регулярные выражения в Rust с примерами: на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Регулярные выражения в Rust с примерами: на практике

Регулярные выражения (regex) — мощный инструмент для работы с текстом. В Rust, несмотря на строгую типизацию и фокус на безопасности, работа с регулярными выражениями реализована эффективно и удобно благодаря крейту regex. В этой статье мы разберем, как использовать регулярные выражения в Rust на практике: от установки до сложных сценариев обработки строк.

Мы рассмотрим основные операции: поиск, извлечение, замену и валидацию. Все примеры кода написаны на Rust и готовы к использованию.

Установка и подключение крейта regex

Для работы с регулярными выражениями в Rust используется сторонний, но очень популярный крейт regex. Он не входит в стандартную библиотеку, поэтому его нужно добавить в проект.

Добавьте зависимость в файл Cargo.toml:

[dependencies]
regex = "1"

После этого в коде можно импортировать основные типы:

use regex::Regex;

Крейт regex компилирует регулярное выражение в конечный автомат, что обеспечивает высокую производительность. Ошибки компиляции шаблона отлавливаются на этапе выполнения (через Result), что делает код надежным.

Основы: поиск совпадений в строке

Самый простой способ проверить, соответствует ли строка шаблону — использовать метод is_match. Рассмотрим пример проверки наличия email-адреса в тексте.

use regex::Regex;

fn main() {
    let re = Regex::new(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b").unwrap();
    let text = "Свяжитесь с нами по адресу support@example.com.";

    if re.is_match(text) {
        println!("Email найден!");
    } else {
        println!("Email не найден.");
    }
}

Метод Regex::new принимает строку с шаблоном и возвращает Result. unwrap() здесь используется для простоты, но в production-коде стоит обрабатывать ошибки корректно.

Для поиска всех совпадений используется метод find_iter, который возвращает итератор по объектам Match:

use regex::Regex;

fn main() {
    let re = Regex::new(r"\d+").unwrap();
    let text = "Цены: 100, 250 и 500 рублей.";

    for mat in re.find_iter(text) {
        println!("Найдено число: {}", mat.as_str());
    }
}

Этот код выведет:

Найдено число: 100
Найдено число: 250
Найдено число: 500

Извлечение данных с помощью групп захвата

Группы захвата (capturing groups) позволяют извлекать не просто всё совпадение, а его отдельные части. Например, разбор даты в формате "дд-мм-гггг".

use regex::Regex;

fn main() {
    let re = Regex::new(r"(\d{2})-(\d{2})-(\d{4})").unwrap();
    let text = "Сегодня 15-03-2024.";

    if let Some(caps) = re.captures(text) {
        println!("День: {}", &caps[1]);
        println!("Месяц: {}", &caps[2]);
        println!("Год: {}", &caps[3]);
    }
}

Индексация групп начинается с 1 (группа 0 — это всё совпадение). Метод captures возвращает Option<Captures>. Если совпадение найдено, мы можем обращаться к группам через индекс.

Для извлечения всех вхождений с группами используется captures_iter:

use regex::Regex;

fn main() {
    let re = Regex::new(r"(\w+)@(\w+)\.(\w+)").unwrap();
    let text = "emails: user@host.com, admin@site.org";

    for caps in re.captures_iter(text) {
        println!("Логин: {}, домен: {}, зона: {}", &caps[1], &caps[2], &caps[3]);
    }
}

Этот подход незаменим при парсинге логов, файлов конфигурации или извлечении структурированных данных из неструктурированного текста.

Замена текста по шаблону

Крейт regex поддерживает замену текста. Метод replace заменяет первое совпадение, а replace_all — все совпадения.

Простая замена:

use regex::Regex;

fn main() {
    let re = Regex::new(r"плохой").unwrap();
    let text = "Это был плохой день.";
    let result = re.replace(text, "хороший");
    println!("{}", result); // Выведет: Это был хороший день.
}

Замена с использованием групп захвата позволяет динамически формировать строку замены:

use regex::Regex;

fn main() {
    let re = Regex::new(r"(\w+) (\w+)").unwrap();
    let text = "Иван Иванов";
    // Меняем местами имя и фамилию
    let result = re.replace(text, "$2 $1");
    println!("{}", result); // Выведет: Иванов Иван
}

В строке замены $1, $2 и т.д. ссылаются на соответствующие группы захвата. Это мощный инструмент для форматирования данных.

Также можно передавать замыкание для более сложной логики замены:

use regex::Regex;

fn main() {
    let re = Regex::new(r"\b\d+\b").unwrap();
    let text = "Числа: 10, 20, 30.";
    let result = re.replace_all(text, |caps: ®ex::Captures| {
        let num: i32 = caps[0].parse().unwrap();
        (num * 2).to_string()
    });
    println!("{}", result); // Выведет: Числа: 20, 40, 60.
}

Валидация строк с помощью регулярных выражений

Регулярные выражения отлично подходят для валидации ввода: проверка email, номера телефона, IP-адреса и т.д. Рассмотрим пример проверки российского номера телефона.

use regex::Regex;

fn validate_phone(phone: &str) -> bool {
    // Шаблон для номеров вида +7 (XXX) XXX-XX-XX или 8XXXXXXXXXX
    let re = Regex::new(r"^(\+7|8)[\s-]?\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{2}[\s-]?\d{                

Блоги

Рекомендаций по книгам