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

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

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

Начать курс

Введение: Зачем нужны регулярные выражения в Rust?

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

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

1. Настройка проекта и первый regex

Добавление зависимости

Для начала добавьте крейт regex в файл Cargo.toml:

[dependencies]
regex = "1"

После этого обновите зависимости командой cargo build.

Первый пример: поиск слова

Напишем простую программу, которая находит слово "Rust" в строке:

use regex::Regex;

fn main() {
    let re = Regex::new(r"Rust").unwrap();
    let text = "Я изучаю Rust и Python.";
    
    if re.is_match(text) {
        println!("Слово 'Rust' найдено!");
    } else {
        println!("Слово не найдено.");
    }
}

Функция Regex::new компилирует шаблон. Если шаблон невалиден, программа завершится с ошибкой (используйте unwrap() только в прототипах).

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

Группировка и именованные группы

Регулярные выражения позволяют извлекать части текста с помощью круглых скобок. Рассмотрим пример с email:

use regex::Regex;

fn main() {
    let re = Regex::new(r"(?P<name>[a-zA-Z0-9._%+-]+)@(?P<domain>[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})").unwrap();
    let text = "Мой email: user@example.com, а также admin@test.org.";
    
    for caps in re.captures_iter(text) {
        println!("Имя: {}", &caps["name"]);
        println!("Домен: {}", &caps["domain"]);
    }
}

В этом примере мы используем именованные группы (?P<name>...). Это делает код более читаемым и удобным для дальнейшей обработки.

Извлечение всех совпадений

Если нужно получить все совпадения без групп, используйте find_iter:

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());
    }
}

Метод find_iter возвращает итератор по объектам Match, которые содержат начальную и конечную позиции, а также сам текст совпадения.

3. Замена текста и сложные шаблоны

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

Заменим все цифры на слово "NUMBER":

use regex::Regex;

fn main() {
    let re = Regex::new(r"\d+").unwrap();
    let text = "Код: 12345, статус: 0.";
    let result = re.replace_all(text, "NUMBER");
    println!("{}", result); // "Код: NUMBER, статус: NUMBER."
}

Замена с использованием захватов

Можно ссылаться на группы в строке замены с помощью $1, $2 или именованных групп $name:

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-03-15";
    let result = re.replace_all(text, "$day.$month.$year");
    println!("{}", result); // "Дата: 15.03.2024"
}

Сложный шаблон: парсинг логов

Рассмотрим реальный пример — парсинг строки лога веб-сервера:

use regex::Regex;

fn main() {
    let log_line = r#"192.168.1.1 - - [10/Mar/2024:12:30:45 +0000] "GET /index.html HTTP/1.1" 200 2326"#;
    let re = Regex::new(
        r#"^(?P<ip>\d+\.\d+\.\d+\.\d+) - - \[(?P<date>[^\]]+)\] "(?P<method>\w+) (?P<path>[^ ]+) HTTP/\d\.\d" (?P<status>\d{3}) (?P<size>\d+)$"#
    ).unwrap();
    
    if let Some(caps) = re.captures(log_line) {
        println!("IP: {}", &caps["ip"]);
        println!("Дата: {}", &caps["date"]);
        println!("Метод: {}", &caps["method"]);
        println!("Путь: {}", &caps["path"]);
        println!("Статус: {}", &caps["status"]);
        println!("Размер: {}", &caps["size"]);
    }
}

Обратите внимание на экранирование кавычек и использование сырых строковых литералов r#"..."# — это упрощает написание сложных шаблонов.

4. Производительность и лучшие практики

Компиляция regex один раз

Если вы используете одно и то же регулярное выражение многократно (например, в цикле), компилируйте его один раз и сохраняйте в переменной. Компиляция — дорогая операция.

Использование макроса regex!

Макрос regex! компилирует шаблон на этапе компиляции программы. Это гарантирует корректность regex и ускоряет выполнение:

use regex::Regex;

fn main() {
    let re = regex!("\\d{3}-\\d{2}-\\d{4}");
    let text = "SSN: 123-45-6789";
    if re.is_match(text) {
        println!("Найден номер SSN.");
    }
}

Для использования макроса добавьте regex с feature macro в Cargo.toml:

[dependencies]
regex = { version =                

Блоги

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