# Pire ## Список функций * `Pire::Grep(pattern:String) -> (string:String?) -> Bool` * `Pire::Match(pattern:String) -> (string:String?) -> Bool` * `Pire::MultiGrep(pattern:String) -> (string:String?) -> Tuple` * `Pire::MultiMatch(pattern:String) -> (string:String?) -> Tuple` * `Pire::Capture(pattern:String) -> (string:String?) -> String?` * `Pire::Replace(pattern:String) -> (string:String?, replacement:String) -> String?` Одной из опций для поиска по регулярным выражениям в YQL является библиотека [Pire](https://github.com/yandex/pire) (Perl Incompatible Regular Expressions). Это разработанная в Яндексе очень быстрая библиотека регулярных выражений: на нижнем уровне она просматривает входную строку один раз, подряд, без откатов, и тратит (на x86 и x86_64) по 5 инструкций на символ. Скорость работы достигается за счет некоторых разумных ограничений: * Библиотека Pire ориентирована прежде всего на проверку на совпадение строки с регулярным выражением (Match). * Возможность вернуть совпавшую подстроку тоже поддерживается (Capture), но с ограничениями (возвращает совпадение только с одной группой). По умолчанию все функции работают в однобайтовом режиме, но если регулярное выражение является валидной UTF-8 строкой, но не является валидной ASCII строкой, — автоматически включается режим UTF-8. Чтобы включить Unicode-режим, можно вставить в выражение один символ за пределами ASCII с оператором `?`, например `\\w+я?`. ## Синтаксис вызова {#call-syntax} Чтобы избежать компиляции регулярного выражения на каждой строке таблицы, необходимо обернуть вызов функции в [именованное выражение](../../syntax/expressions.md#named-nodes): ```yql $re = Pire::Grep("\\d+"); -- создаем вызываемое значение для проверки конкретного регулярного выражения SELECT * FROM table WHERE $re(key); -- используем его для фильтрации таблицы ``` {% note alert %} При экранировании спецсимволов в регулярном выражении нужен второй слеш, так как все стандартные строковые литералы в SQL могут принимать С-escaped строки, а последовательность `\d` не является валидной последовательностью, и даже если бы являлась — не приводила бы к ожидаемому эффекту поиска чисел. {% endnote %} Есть возможность отключить чувствительность к регистру (то есть включить case-insensitive режим), указав в начале регулярного выражения флаг `(?i)`. ### Примеры ```yql $value = "xaaxaaxaa"; $match = Pire::Match("a.*"); $grep = Pire::Grep("axa"); $insensitive_grep = Pire::Grep("(?i)axa"); $multi_match = Pire::MultiMatch(@@a.* .*a.* .*a .*axa.*@@); $capture = Pire::Capture(".*x(a).*"); $capture_many = Pire::Capture(".*x(a+).*"); $replace = Pire::Replace(".*x(a).*"); SELECT $match($value) AS match, -- false $grep($value) AS grep, -- true $insensitive_grep($value) AS insensitive_grep, -- true $multi_match($value) AS multi_match, -- (false, true, true, true) $multi_match($value).0 AS some_multi_match, -- false $capture($value) AS capture, -- "a" $capture_many($value) AS capture_many, -- "aa" $replace($value, "b") AS replace; -- "xaaxaaxba" ``` ## Grep {#grep} Проверяет совпадение регулярного выражения с **частью строки** (произвольной подстрокой). ## Match {#match} Проверяет совпадение регулярного выражения **со строкой целиком**. Чтобы получить результат, аналогичный `Grep` (где учитывается совпадение с подстрокой), нужно обернуть регулярное выражение с обоих сторон в `.*`, например `.*foo.*` вместо `foo`. ## MultiGrep / MultiMatch {#multigrep} Библиотека Pire предоставляет возможность за один проход по тексту проверить несколько регулярных выражений и получить по каждому из них отдельный ответ. С помощью функций MultiGrep/MultiMatch можно оптимизировать скорость выполнения запроса, но это нужно делать осмотрительно, поскольку размер используемого для проверки конечного автомата растет экспоненциально с числом регулярных выражений: * Если вас интересует совпадение строки с любым из перечисленных выражений (результаты объединяются через «или»), то намного эффективнее сделать одно регулярное выражение, объединив части с помощью оператора `|`, и использовать для поиска обычный Grep или Match. * В библиотеке Pire установлен лимит на размер конечного автомата (в YQL используется значение этого лимита, установленное по умолчанию в библиотеке). Если лимит превышен, при запуске запроса вернется ошибка `Failed to glue up regexes, probably the finite state machine appeared to be too large`. При вызове функций MultiGrep/MultiMatch регулярные выражения передаются по одному на строку с использованием [многострочных строковых литералов](../../syntax/expressions.md#multiline-string-literals): ### Примеры ```yql $multi_match = Pire::MultiMatch(@@a.* .*x.* .*axa.*@@); SELECT $multi_match("a") AS a, -- (true, false, false) $multi_match("axa") AS axa; -- (true, true, true) ``` ## Capture {#capture} При совпадении строки с указанным регулярным выражением возвращает подстроку, совпавшую с группой, отмеченной в регулярном выражении круглыми скобками. Capture работает НЕ в жадном режиме, то есть возвращается самая короткая подстрока из возможных. {% note alert %} В выражении должна быть ровно **одна** группа, обозначенная круглыми скобками. В случае отсутствия совпадения возвращается `NULL` (пустой Optional). {% endnote %} Если описанные выше ограничения и особенности по каким-либо причинам выглядят неприемлемыми, рекомендуется рассмотреть возможность использования [Re2::Capture](re2.md#capture). ## Replace {#replace} В библиотеке Pire отсутствует функция замены по регулярному выражению. Функция `Pire::Replace` в YQL представляет собой упрощенную эмуляцию, реализованную с помощью `Capture`. Может работать некорректно, если найденная подстрока встречается в исходной более одного раза. Как правило, лучше воспользоваться [Re2::Replace](re2.md#replace) вместо неё.