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

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

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

Начать курс

Регулярные выражения в 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 по умолчанию. Однако вы можете управлять его поведением с

Блоги

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