grex هي مكتبة بالإضافة إلى أداة مساعدة لسطر الأوامر تهدف إلى تبسيط المهمة المعقدة والمملة في كثير من الأحيان لإنشاء التعبيرات العادية. يقوم بذلك عن طريق إنشاء تعبير عادي واحد تلقائيًا من حالات الاختبار المقدمة من قبل المستخدم. يتم ضمان أن يتطابق التعبير الناتج مع حالات الاختبار التي تم إنشاؤها منها.
بدأ هذا المشروع كمنفذ Rust لأداة JavaScript regexgen التي كتبها Devon Govett. على الرغم من إمكانية إضافة الكثير من الميزات المفيدة إليه، إلا أنه يبدو أن تطويره قد توقف منذ عدة سنوات. تتمثل الخطة الآن في إضافة هذه الميزات الجديدة إلى grex حيث يتألق Rust حقًا عندما يتعلق الأمر بأدوات سطر الأوامر. يقدم grex جميع الميزات التي يوفرها regexgen والمزيد.
تتمثل فلسفة هذا المشروع في إنشاء التعبير العادي الأكثر تحديدًا بشكل افتراضي والذي يتطابق تمامًا مع المدخلات المحددة فقط ولا شيء آخر. باستخدام علامات سطر الأوامر (في أداة CLI) أو أساليب المعالجة المسبقة (في المكتبة)، يمكن إنشاء تعبيرات أكثر عمومية.
التعبيرات المنتجة هي تعبيرات عادية متوافقة مع Perl والتي تتوافق أيضًا مع محلل التعبير العادي في صندوق Rust's regex . لم يتم اختبار موزعي التعبير العادي أو المكتبات المعنية من لغات البرمجة الأخرى حتى الآن، ولكن يجب أن تكون متوافقة في الغالب أيضًا.
بالتأكيد، نعم! باستخدام الإعدادات القياسية، يُنتج grex تعبيرًا عاديًا يضمن تطابقه فقط مع حالات الاختبار المقدمة كمدخلات ولا شيء غير ذلك. وقد تم التحقق من ذلك عن طريق اختبارات الملكية. ومع ذلك، إذا تم تمكين التحويل إلى فئات الأحرف المختصرة مثل w
، فإن التعبير العادي الناتج يطابق نطاقًا أوسع بكثير من حالات الاختبار. تعد المعرفة بعواقب هذا التحويل أمرًا ضروريًا للعثور على تعبير عادي صحيح لمجال عملك.
يستخدم grex خوارزمية تحاول العثور على أقصر تعبير عادي ممكن لحالات الاختبار المحددة. ومع ذلك، في كثير من الأحيان، يظل التعبير الناتج أطول أو أكثر تعقيدًا مما يجب أن يكون. في مثل هذه الحالات، لا يمكن إنشاء تعبير عادي أكثر إحكاما أو أناقة إلا باليد. بالإضافة إلى ذلك، يحتوي كل محرك تعبير عادي على تحسينات مدمجة مختلفة. لا يعرف grex أي شيء عن هذه الأشياء، وبالتالي لا يمكنه تحسين تعبيراته التعبيرية لمحرك معين.
لذا، من فضلك تعلم كيفية كتابة التعبيرات العادية! أفضل حالة استخدام حاليًا لـ grex هي العثور على regex الأولي الصحيح الذي يجب فحصه يدويًا إذا كان من الممكن إجراء المزيد من التحسينات.
{min,max}
|
مشغل?
محدد الكمية^
و $
يمكنك تنزيل الملف القابل للتنفيذ القائم بذاته لنظامك الأساسي أعلاه ووضعه في المكان الذي تختاره. وبدلاً من ذلك، تتوفر ثنائيات 64 بت المجمعة مسبقًا ضمن مديري الحزم Scoop (لنظام Windows)، وHomebrew (لنظامي macOS وLinux)، وMacPorts (لنظام macOS)، وHuber (لنظام macOS، وLinux، وWindows). ساهم Raúl Piracés في تقديم حزمة Chocolatey Windows.
يتم استضافة grex أيضًا على موقعcrats.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 }
تصفيق التبعية مطلوب فقط لأداة سطر الأوامر. من خلال تعطيل الميزات الافتراضية، يتم منع تنزيل وتجميع التصفيق للمكتبة.
يتم توفير شرح تفصيلي للإعدادات المتاحة في قسم المكتبة. يمكن دمج جميع الإعدادات بحرية مع بعضها البعض.
يتم تمرير حالات الاختبار إما مباشرة ( 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})$" ) ;
تعيين حد أدنى لعدد التكرار مرتين في المثال التالي، سيتم تحويل حالة الاختبار 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 تسلسلات هروب Unicode لمستويات الكود النجمي (النطاق 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?)?" ) ;
توضح الأمثلة التالية ميزات بناء جملة regex المدعومة المتنوعة:
$ 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. يرجى ملاحظة : يلزم وجود Rust >= 1.70.0 لإنشاء واجهة سطر الأوامر (CLI). بالنسبة لجزء المكتبة، يعتبر 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 Package Index ويمكن تثبيته مع:
pip install grex
لإنشاء وحدة امتداد Python بنفسك، قم بإنشاء بيئة افتراضية وقم بتثبيت Maturin.
python -m venv /path/to/virtual/environment
source /path/to/virtual/environment/bin/activate
pip install maturin
maturin build
تحتوي مكتبة بايثون على فئة واحدة تسمى 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 الخاصة بـ Hopcroft.
يتم التعبير عن DFA المصغر كنظام من المعادلات الخطية التي يتم حلها باستخدام طريقة Brzozowski الجبرية، مما يؤدي إلى التعبير العادي النهائي.
ألق نظرة على القضايا المخطط لها.
في حال كنت تريد المساهمة بشيء ما في Grex ، فأنا أشجعك على القيام بذلك. هل لديك أفكار لميزات رائعة؟ أو هل وجدت أي أخطاء حتى الآن؟ لا تتردد في فتح قضية أو إرسال طلب سحب. إنه موضع تقدير كبير جدًا. :-)