Pire
Список функций
Pire::Grep(pattern:String) -> (string:String?) -> Bool
Pire::Match(pattern:String) -> (string:String?) -> Bool
Pire::MultiGrep(pattern:String) -> (string:String?) -> Tuple<Bool, Bool, ...>
Pire::MultiMatch(pattern:String) -> (string:String?) -> Tuple<Bool, Bool, ...>
Pire::Capture(pattern:String) -> (string:String?) -> String?
Pire::Replace(pattern:String) -> (string:String?, replacement:String) -> String?
Одной из опций для поиска по регулярным выражениям в YQL является библиотека Pire (Perl Incompatible Regular Expressions). Это разработанная в Яндексе очень быстрая библиотека регулярных выражений: на нижнем уровне она просматривает входную строку один раз, подряд, без откатов, и тратит (на x86 и x86_64) по 5 инструкций на символ.
Скорость работы достигается за счет некоторых разумных ограничений:
- Библиотека Pire ориентирована прежде всего на проверку на совпадение строки с регулярным выражением (Match).
- Возможность вернуть совпавшую подстроку тоже поддерживается (Capture), но с ограничениями (возвращает совпадение только с одной группой).
По умолчанию все функции работают в однобайтовом режиме, но если регулярное выражение является валидной UTF-8 строкой, но не является валидной ASCII строкой, — автоматически включается режим UTF-8.
Чтобы включить Unicode-режим, можно вставить в выражение один символ за пределами ASCII с оператором ?
, например \\w+я?
.
Синтаксис вызова
Чтобы избежать компиляции регулярного выражения на каждой строке таблицы, необходимо обернуть вызов функции в именованное выражение:
$re = Pire::Grep("\\d+"); -- создаем вызываемое значение для проверки конкретного регулярного выражения
SELECT * FROM table WHERE $re(key); -- используем его для фильтрации таблицы
Внимание
При экранировании спецсимволов в регулярном выражении нужен второй слеш, так как все стандартные строковые литералы в SQL могут принимать С-escaped строки, а последовательность \d
не является валидной последовательностью, и даже если бы являлась — не приводила бы к ожидаемому эффекту поиска чисел.
Есть возможность отключить чувствительность к регистру (то есть включить case-insensitive режим), указав в начале регулярного выражения флаг (?i)
.
Примеры
$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
Проверяет совпадение регулярного выражения с частью строки (произвольной подстрокой).
Match
Проверяет совпадение регулярного выражения со строкой целиком.
Чтобы получить результат, аналогичный Grep
(где учитывается совпадение с подстрокой), нужно обернуть регулярное выражение с обоих сторон в .*
, например .*foo.*
вместо foo
.
MultiGrep / MultiMatch
Библиотека Pire предоставляет возможность за один проход по тексту проверить несколько регулярных выражений и получить по каждому из них отдельный ответ.
С помощью функций MultiGrep/MultiMatch можно оптимизировать скорость выполнения запроса, но это нужно делать осмотрительно, поскольку размер используемого для проверки конечного автомата растет экспоненциально с числом регулярных выражений:
- Если вас интересует совпадение строки с любым из перечисленных выражений (результаты объединяются через «или»), то намного эффективнее сделать одно регулярное выражение, объединив части с помощью оператора
|
, и использовать для поиска обычный Grep или Match. - В библиотеке Pire установлен лимит на размер конечного автомата (в YQL используется значение этого лимита, установленное по умолчанию в библиотеке). Если лимит превышен, при запуске запроса вернется ошибка
Failed to glue up regexes, probably the finite state machine appeared to be too large
.
При вызове функций MultiGrep/MultiMatch регулярные выражения передаются по одному на строку с использованием многострочных строковых литералов:
Примеры
$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 работает НЕ в жадном режиме, то есть возвращается самая короткая подстрока из возможных.
Внимание
В выражении должна быть ровно одна группа, обозначенная круглыми скобками. В случае отсутствия совпадения возвращается NULL
(пустой Optional).
Если описанные выше ограничения и особенности по каким-либо причинам выглядят неприемлемыми, рекомендуется рассмотреть возможность использования Re2::Capture.
Replace
В библиотеке Pire отсутствует функция замены по регулярному выражению. Функция Pire::Replace
в YQL представляет собой упрощенную эмуляцию, реализованную с помощью Capture
. Может работать некорректно, если найденная подстрока встречается в исходной более одного раза.
Как правило, лучше воспользоваться Re2::Replace вместо неё.