grex — это библиотека, а также утилита командной строки, предназначенная для упрощения зачастую сложной и утомительной задачи создания регулярных выражений. Это достигается путем автоматической генерации одного регулярного выражения на основе предоставленных пользователем тестовых примеров. Полученное выражение гарантированно соответствует тестовым примерам, из которых оно было создано.
Этот проект начался как порт на Rust инструмента regexgen для JavaScript, написанного Девоном Говеттом. Хотя к нему можно было бы добавить множество дополнительных полезных функций, его разработка, по-видимому, была прекращена несколько лет назад. Сейчас мы планируем добавить эти новые функции в grex, поскольку Rust действительно хорош, когда дело касается инструментов командной строки. grex предлагает все функции, которые предоставляет regexgen , и даже больше.
Философия этого проекта заключается в том, чтобы по умолчанию генерировать максимально конкретное регулярное выражение, которое точно соответствует только заданному вводу и ничему больше. С помощью флагов командной строки (в инструменте CLI) или методов предварительной обработки (в библиотеке) можно создавать более обобщенные выражения.
Создаваемые выражения являются регулярными выражениями, совместимыми с Perl, которые также совместимы с анализатором регулярных выражений в контейнере регулярных выражений Rust. Другие парсеры регулярных выражений или соответствующие библиотеки из других языков программирования пока не тестировались, но они также должны быть в основном совместимы.
Определенно, да! Используя стандартные настройки, grex создает регулярное выражение, которое гарантированно соответствует только тестовым примерам, заданным в качестве входных данных, и ничему больше. Это подтверждено испытаниями свойств. Однако если включено преобразование в классы сокращенных символов, такие как w
, полученное регулярное выражение соответствует гораздо более широкому кругу тестовых примеров. Знание последствий этого преобразования необходимо для поиска правильного регулярного выражения для вашего бизнес-домена.
grex использует алгоритм, который пытается найти кратчайшее возможное регулярное выражение для заданных тестовых случаев. Однако очень часто результирующее выражение оказывается длиннее или сложнее, чем должно быть. В таких случаях более компактное или элегантное регулярное выражение можно создать только вручную. Кроме того, каждый механизм регулярных выражений имеет разные встроенные оптимизации. grex ничего о них не знает и поэтому не может оптимизировать свои регулярные выражения для конкретного движка.
Итак, пожалуйста, научитесь писать регулярные выражения! На данный момент лучший вариант использования grex — найти первоначальное правильное регулярное выражение, которое следует проверить вручную, если возможна дальнейшая оптимизация.
{min,max}
|
оператор?
квантификатор^
и $
Вы можете скачать автономный исполняемый файл для своей платформы выше и поместить его в любое место по вашему выбору. Альтернативно, предварительно скомпилированные 64-битные двоичные файлы доступны в менеджерах пакетов Scoop (для Windows), Homebrew (для macOS и Linux), MacPorts (для macOS) и Huber (для macOS, Linux и Windows). Рауль Пирасес предоставил пакет Chocolatey Windows.
grex также размещен на crates.io, официальном реестре пакетов Rust. Если вы являетесь разработчиком Rust и у вас уже установлена цепочка инструментов Rust, вы можете установить ее путем компиляции из исходного кода с помощью Cargo , менеджера пакетов Rust. Итак, краткий обзор вариантов установки:
( brew | cargo | choco | huber | port | scoop ) install grex
Чтобы использовать grex в качестве библиотеки, просто добавьте ее как зависимость к вашему файлу Cargo.toml
:
[ dependencies ]
grex = { version = " 1.4.5 " , default-features = false }
Хлопок зависимости необходим только для инструмента командной строки. При отключении функций по умолчанию загрузка и компиляция clap для библиотеки невозможна.
Подробные пояснения доступных настроек представлены в разделе библиотеки. Все настройки можно свободно комбинировать между собой.
Тестовые случаи передаются либо напрямую ( grex abc
), либо из файла ( grex -f test_cases.txt
). grex также может получать входные данные из конвейеров Unix, например cat test_cases.txt | grex -
.
В следующей таблице показаны все доступные флаги и параметры:
$ grex -h
grex 1.4.5
© 2019-today Peter M. Stahl
Licensed under the Apache License, Version 2.0
Downloadable from https://crates.io/crates/grex
Source code at https://github.com/pemistahl/grex
grex generates regular expressions from user-provided test cases.
Usage: grex [OPTIONS] {INPUT...|--file }
Input:
[INPUT]... One or more test cases separated by blank space
-f, --file Reads test cases on separate lines from a file
Digit Options:
-d, --digits Converts any Unicode decimal digit to d
-D, --non-digits Converts any character which is not a Unicode decimal digit to D
Whitespace Options:
-s, --spaces Converts any Unicode whitespace character to s
-S, --non-spaces Converts any character which is not a Unicode whitespace character to S
Word Options:
-w, --words Converts any Unicode word character to w
-W, --non-words Converts any character which is not a Unicode word character to W
Escaping Options:
-e, --escape Replaces all non-ASCII characters with unicode escape sequences
--with-surrogates Converts astral code points to surrogate pairs if --escape is set
Repetition Options:
-r, --repetitions
Detects repeated non-overlapping substrings and converts them to {min,max} quantifier
notation
--min-repetitions
Specifies the minimum quantity of substring repetitions to be converted if --repetitions
is set [default: 1]
--min-substring-length
Specifies the minimum length a repeated substring must have in order to be converted if
--repetitions is set [default: 1]
Anchor Options:
--no-start-anchor Removes the caret anchor `^` from the resulting regular expression
--no-end-anchor Removes the dollar sign anchor `$` from the resulting regular expression
--no-anchors Removes the caret and dollar sign anchors from the resulting regular
expression
Display Options:
-x, --verbose Produces a nicer-looking regular expression in verbose mode
-c, --colorize Provides syntax highlighting for the resulting regular expression
Miscellaneous Options:
-i, --ignore-case Performs case-insensitive matching, letters match both upper and lower case
-g, --capture-groups Replaces non-capturing groups with capturing ones
-h, --help Prints help information
-v, --version Prints version information
Тестовые случаи передаются либо из коллекции через RegExpBuilder::from()
, либо из файла через RegExpBuilder::from_file()
. При чтении из файла каждый тестовый пример должен находиться на отдельной строке. Строки могут заканчиваться либо символом новой строки n
, либо возвратом каретки с переводом строки rn
.
use grex :: RegExpBuilder ;
let regexp = RegExpBuilder :: from ( & [ "a" , "aa" , "aaa" ] ) . build ( ) ;
assert_eq ! ( regexp , "^a(?:aa?)?$" ) ;
use grex :: RegExpBuilder ;
let regexp = RegExpBuilder :: from ( & [ "a" , "aa" , "123" ] )
. with_conversion_of_digits ( )
. with_conversion_of_words ( )
. build ( ) ;
assert_eq ! ( regexp , "^( \ d \ d \ d| \ w(?: \ w)?)$" ) ;
use grex :: RegExpBuilder ;
let regexp = RegExpBuilder :: from ( & [ "aa" , "bcbc" , "defdefdef" ] )
. with_conversion_of_repetitions ( )
. build ( ) ;
assert_eq ! ( regexp , "^(?:a{2}|(?:bc){2}|(?:def){3})$" ) ;
По умолчанию grex преобразует таким образом каждую подстроку длиной не менее одного символа и которая впоследствии повторяется хотя бы один раз. При желании вы можете настроить эти два параметра.
В следующем примере тестовый пример aa
не преобразуется в a{2}
поскольку повторяющаяся подстрока a
имеет длину 1, но минимальная длина подстроки установлена равной 2.
use grex :: RegExpBuilder ;
let regexp = RegExpBuilder :: from ( & [ "aa" , "bcbc" , "defdefdef" ] )
. with_conversion_of_repetitions ( )
. with_minimum_substring_length ( 2 )
. build ( ) ;
assert_eq ! ( regexp , "^(?:aa|(?:bc){2}|(?:def){3})$" ) ;
Если в следующем примере установить минимальное количество повторений в 2, будет преобразован только тестовый пример defdefdef
, поскольку он единственный повторяется дважды.
use grex :: RegExpBuilder ;
let regexp = RegExpBuilder :: from ( & [ "aa" , "bcbc" , "defdefdef" ] )
. with_conversion_of_repetitions ( )
. with_minimum_repetitions ( 2 )
. build ( ) ;
assert_eq ! ( regexp , "^(?:bcbc|aa|(?:def){3})$" ) ;
use grex :: RegExpBuilder ;
let regexp = RegExpBuilder :: from ( & [ "You smell like ?." ] )
. with_escaping_of_non_ascii_chars ( false )
. build ( ) ;
assert_eq ! ( regexp , "^You smell like \ u{1f4a9} \ .$" ) ;
Старые версии JavaScript не поддерживают escape-последовательности Юникода для плоскостей астрального кода (диапазон U+010000
до U+10FFFF
). Для поддержки этих символов в регулярных выражениях JavaScript необходимо преобразование в суррогатные пары. Более подробную информацию по этому вопросу можно найти здесь.
use grex :: RegExpBuilder ;
let regexp = RegExpBuilder :: from ( & [ "You smell like ?." ] )
. with_escaped_non_ascii_chars ( true )
. build ( ) ;
assert_eq ! ( regexp , "^You smell like \ u{d83d} \ u{dca9} \ .$" ) ;
Регулярные выражения, генерируемые grex , по умолчанию чувствительны к регистру. Сопоставление без учета регистра можно включить следующим образом:
use grex :: RegExpBuilder ;
let regexp = RegExpBuilder :: from ( & [ "big" , "BIGGER" ] )
. with_case_insensitive_matching ( )
. build ( ) ;
assert_eq ! ( regexp , "(?i)^big(?:ger)?$" ) ;
По умолчанию используются группы без захвата. Расширяя предыдущий пример, вы можете вместо этого переключиться на захват групп.
use grex :: RegExpBuilder ;
let regexp = RegExpBuilder :: from ( & [ "big" , "BIGGER" ] )
. with_case_insensitive_matching ( )
. with_capturing_groups ( )
. build ( ) ;
assert_eq ! ( regexp , "(?i)^big(ger)?$" ) ;
Если сгенерированное регулярное выражение кажется вам трудным для чтения, вы можете включить подробный режим. Затем выражение помещается в несколько строк и выравнивается с отступом, чтобы сделать его более приятным для глаз.
use grex :: RegExpBuilder ;
use indoc :: indoc ;
let regexp = RegExpBuilder :: from ( & [ "a" , "b" , "bcd" ] )
. with_verbose_mode ( )
. build ( ) ;
assert_eq ! ( regexp , indoc! (
r#"
(?x)
^
(?:
b
(?:
cd
)?
|
a
)
$"#
) ) ;
По умолчанию привязки ^
и $
помещаются вокруг каждого сгенерированного регулярного выражения, чтобы гарантировать, что оно соответствует только тестовым примерам, заданным в качестве входных данных. Однако достаточно часто желательно использовать сгенерированный шаблон как часть более крупного. Для этого якоря можно отключить как по отдельности, так и оба.
use grex :: RegExpBuilder ;
let regexp = RegExpBuilder :: from ( & [ "a" , "aa" , "aaa" ] )
. without_anchors ( )
. build ( ) ;
assert_eq ! ( regexp , "a(?:aa?)?" ) ;
В следующих примерах показаны различные поддерживаемые функции синтаксиса регулярных выражений:
$ grex a b c
^[a-c]$
$ grex a c d e f
^[ac-f]$
$ grex a b x de
^( ? :de | [abx])$
$ grex abc bc
^a ? bc$
$ grex a b bc
^( ? :bc ? | a)$
$ grex [a-z]
^ [ a - z ] $
$ grex -r b ba baa baaa
^b( ? :a{1,3}) ? $
$ grex -r b ba baa baaaa
^b( ? :a{1,2} | a{4}) ? $
$ grex y̆ a z
^( ? :y̆ | [az])$
Note:
Grapheme y̆ consists of two Unicode symbols:
U+0079 (Latin Small Letter Y)
U+0306 (Combining Breve)
$ grex " I ♥ cake " " I ♥ cookies "
^I ♥ c( ? :ookies | ake)$
Note:
Input containing blank space must be
surrounded by quotation marks.
Строка "I ♥♥♥ 36 and ٣ and ??."
служит входными данными для следующих примеров с использованием обозначения командной строки:
$ grex < INPUT >
^I ♥♥♥ 36 and ٣ and ?? . $
$ grex -e < INPUT >
^I u {2665} u {2665} u {2665} 36 and u {663} and u {1f4a9} u {1f4a9} . $
$ grex -e --with-surrogates < INPUT >
^I u {2665} u {2665} u {2665} 36 and u {663} and u {d83d} u {dca9} u {d83d} u {dca9} . $
$ grex -d < INPUT >
^I ♥♥♥ dd and d and ?? . $
$ grex -s < INPUT >
^I s ♥♥♥ s 36 s and s ٣ s and s ?? . $
$ grex -w < INPUT >
^ w ♥♥♥ ww www w www ?? . $
$ grex -D < INPUT >
^ DDDDDD 36 DDDDD ٣ DDDDDDDD $
$ grex -S < INPUT >
^ S SSS SS SSS S SSS SSS $
$ grex -dsw < INPUT >
^ ws ♥♥♥ sddswwwsdswwws ?? . $
$ grex -dswW < INPUT >
^ wsWWWsddswwwsdswwwsWWW $
$ grex -r < INPUT >
^I ♥{3} 36 and ٣ and ?{2} . $
$ grex -er < INPUT >
^I u {2665}{3} 36 and u {663} and u {1f4a9}{2} . $
$ grex -er --with-surrogates < INPUT >
^I u {2665}{3} 36 and u {663} and ( ? : u {d83d} u {dca9}){2} . $
$ grex -dgr < INPUT >
^I ♥{3} d ( d and ){2}?{2} . $
$ grex -rs < INPUT >
^I s ♥{3} s 36 s and s ٣ s and s ?{2} . $
$ grex -rw < INPUT >
^ w ♥{3} w ( ? : w w {3} ){2}?{2} . $
$ grex -Dr < INPUT >
^ D {6}36 D {5}٣ D {8}$
$ grex -rS < INPUT >
^ S S ( ? : S {2} ){2} S {3} S S {3} S {3}$
$ grex -rW < INPUT >
^I W {5}36 W and W ٣ W and W {4}$
$ grex -drsw < INPUT >
^ ws ♥{3} sd ( ? : dsw {3} s ){2}?{2} . $
$ grex -drswW < INPUT >
^ wsW {3} sd ( ? : dsw {3} s ){2} W {3}$
Чтобы самостоятельно собрать исходный код, вам понадобится стабильная цепочка инструментов Rust, установленная на вашем компьютере, чтобы был доступен груз , менеджер пакетов Rust. Обратите внимание : для сборки CLI требуется Rust >= 1.70.0. Для библиотечной части достаточно Rust < 1.70.0.
git clone https://github.com/pemistahl/grex.git
cd grex
cargo build
Исходный код сопровождается обширным набором тестов, состоящим из модульных тестов, интеграционных тестов и тестов свойств. Чтобы запустить их, просто скажите:
cargo test
Тесты, измеряющие производительность нескольких настроек, можно запустить с помощью:
cargo bench
С помощью PyO3 и Maturin библиотека была скомпилирована в модуль расширения Python, поэтому ее можно использовать и в любом программном обеспечении Python. Он доступен в индексе пакетов Python и может быть установлен с помощью:
pip install grex
Чтобы создать модуль расширения Python самостоятельно, создайте виртуальную среду и установите Maturin.
python -m venv /path/to/virtual/environment
source /path/to/virtual/environment/bin/activate
pip install maturin
maturin build
Библиотека Python содержит единственный класс с именем RegExpBuilder
, который можно импортировать следующим образом:
from grex import RegExpBuilder
Эту библиотеку можно скомпилировать в WebAssembly (WASM), что позволяет использовать grex в любом проекте на основе JavaScript, будь то в браузере или в серверной части, работающей на Node.js.
Самый простой способ компиляции — использовать wasm-pack
. После установки вы можете, например, собрать библиотеку с веб-целью, чтобы ее можно было использовать непосредственно в браузере:
wasm-pack build --target web
При этом на верхнем уровне этого репозитория создается каталог с именем pkg
, содержащий скомпилированные файлы Wasm и привязки JavaScript и TypeScript. В HTML-файле вы можете вызвать grex , например, следующим образом:
< script type =" module " >
import init , { RegExpBuilder } from "./pkg/grex.js" ;
init ( ) . then ( _ => {
alert ( RegExpBuilder . from ( [ "hello" , "world" ] ) . build ( ) ) ;
} ) ;
script >
Также доступны некоторые интеграционные тесты как для Node.js, так и для браузеров Chrome, Firefox и Safari. Чтобы запустить их, просто скажите:
wasm-pack test --node --headless --chrome --firefox --safari
Если тесты не запускаются в Safari, вам необходимо сначала включить веб-драйвер Safari, выполнив:
sudo safaridriver --enable
Выходные данные wasm-pack
будут размещены в отдельном репозитории, что позволит добавлять дополнительные конфигурации, тесты и документацию, связанные с JavaScript. Затем grex также будет добавлен в реестр npm, что позволит легко загружать и устанавливать его в каждом проекте JavaScript или TypeScript.
Существует демо-сайт, на котором вы можете попробовать grex.
Детерминированный конечный автомат (DFA) создается из входных строк.
Количество состояний и переходов между состояниями в DFA уменьшается за счет применения алгоритма минимизации DFA Хопкрофта.
Минимизированный ДКА выражается как система линейных уравнений, которые решаются алгебраическим методом Бжозовского, что приводит к окончательному регулярному выражению.
Ознакомьтесь с запланированными задачами.
Если вы хотите внести свой вклад в grex , я советую вам это сделать. У вас есть идеи для интересных функций? Или вы уже нашли какие-то ошибки? Не стесняйтесь открыть проблему или отправить запрос на включение. Это очень ценно. :-)