شما اصول اولیه را از قبل میدانید: کلاسهای کاراکتر مانند \d و [A-Z]، کمیتسازهایی مانند {4} و +، و واژههای matches?، all-matching-subseqs و re-replace. این تمرین بخشهایی از عبارات باقاعده را اضافه میکند که ساختار و بافت را توصیف میکنند. این بخشها در همان واژگان regexp قرار دارند و با همان نوشتار R/ / نوشته میشوند.
«لنگرها» بهجای یک کاراکتر، یک موقعیت را تطبیق میدهند:
^ ! the start of the string
$ ! the end of the string
\b ! a word boundary
چون $ به معنای پایان رشته است، وقتی منظورتان علامت دلار واقعی است \$ بنویسید.
یک «مرز واژه» (\b) نقطهای است که یک «کاراکتر واژه» به یک کاراکتر غیرواژه میرسد. کاراکترهای واژه حروف، رقمها و زیرخط هستند؛ هر چیز دیگری (فاصلهها، نشانهگذاری و دو سر رشته) مرز به حساب میآید. پس \bcat\b تنها وقتی cat را تطبیق میدهد که واژه بهتنهایی بایستد:
USING: regexp ;
"cat nap" R/ \bcat\b/ re-contains? . ! => t
"scatter" R/ \bcat\b/ re-contains? . ! => f (cat sits inside a word)
پرانتزها ( ) بخشی از یک الگو را گروهبندی میکنند تا یک کمیتساز، یا یک | («این یا آن»)، به کل گروه اعمال شود:
USING: regexp ;
"abab" R/ (ab)+/ matches? . ! => t (+ applies to "ab")
"dog" R/ cat|dog/ matches? . ! => t (either word)
"$100.50" R/ \$\d+(\.\d{2})?/ matches? . ! => t
"$100" R/ \$\d+(\.\d{2})?/ matches? . ! => t (the group is optional)
الگوی آخر اینطور خوانده میشود: «یک علامت دلار، یک یا چند رقم، و سپس یک گروه اختیاری از یک نقطه و دو رقم».
توجه: در Factor، یک گروه فقط الگو را ساختاردهی میکند. برخلاف برخی ابزارهای عبارات باقاعده، نمیتوانید متنی را که یک گروه تطبیق داده است بیرون بکشید؛ استخراج گروه ثبت وجود ندارد و بازارجاعهایی مانند
\1پشتیبانی نمیشوند. برای بیرون کشیدن متن پیرامون یک نشانه، بهجای آن از جستوجوی پیرامونی استفاده کنید (در ادامه).
جستوجوی پیرامونی بررسی میکند چه چیزی درست پیش یا درست پس از موقعیت جاری میآید، بدون آنکه این کاراکترها بخشی از تطبیق شوند. این جستوجو «عرض صفر» است: آنچه میآزماید در نتیجه گنجانده نمیشود.
(?=...) ! lookahead: ... must follow
(?!...) ! negative lookahead: ... must NOT follow
(?<=...) ! lookbehind: ... must come before
(?<!...) ! negative lookbehind: ... must NOT come before
جستوجوی پیرامونی گزینشی است: همان کاراکترها بسته به اینکه چه چیزی کنارشان قرار دارد تطبیق میخورند یا نه. یک \d+ ساده همهی عددها را برمیدارد، اما پسنگری و پیشنگری فقط آنهایی را نگه میدارند که همسایهی مناسب دارند:
USING: regexp ;
"3 for $10 or 10 for $30" R/ \d+/ all-matching-subseqs .
! => { "3" "10" "10" "30" } (every number)
"3 for $10 or 10 for $30" R/ (?<=\$)\d+/ all-matching-subseqs .
! => { "10" "30" } (only the prices — the bare 3 and 10 drop)
"buy 2 at 15% off, 4 at 30% off" R/ \d+(?=%)/ all-matching-subseqs .
! => { "15" "30" } (only the discounts — the bare 2 and 4 drop)
برای تغییر رفتار کل الگو، یک حرف پس از / پایانی اضافه کنید. i آن را بیاعتنا به بزرگی و کوچکی حروف میکند:
USING: regexp ;
"REFUND" R/ refund/i matches? . ! => t
"Refund" R/ refund/i matches? . ! => t
all-matching-subseqs متن تطبیقخورده را به شما میدهد. map-matches یک قدم فراتر میرود: یک quotation را روی هر تطبیق اجرا میکند و نتیجهها را جمع میکند. این quotation اندیس start تطبیق، اندیس end آن و کل رشته را دریافت میکند؛ سپس subseq این سه را به متن تطبیقخورده تبدیل میکند و شما آن را تبدیل میکنید:
map-matches ( string regexp quot: ( start end string -- obj ) -- seq )
USING: math regexp sequences ;
"a1 b22 c333" R/ \d+/ [ subseq length ] map-matches .
! => { 1 2 3 } (the length of each run of digits)
همتای عوارضمحور آن، each-match، یک quotation را روی هر تطبیق اجرا میکند بدون اینکه چیزی جمع کند.
شما دفاتر حسابهای یک شرکت کوچک را نگه میدارید و تراکنشهای هر روز به شکل خطوط نامرتبی از متن میرسند. از «عبارات باقاعده» استفاده میکنید تا مبلغها را اعتبارسنجی کنید، اعداد را با نمادی که مشخصشان میکند استخراج کنید و خطوطی را که به بررسی دقیقتری نیاز دارند علامت بزنید.
یک مبلغ خوشساختار از یک $، یک یا چند رقم، و یک بخش سِنت اختیاری تشکیل شده که یک نقطه و دقیقاً دو رقم است: $100 یا $100.50، اما نه $100.5. تابع valid-amount? را تعریف کنید که وقتی کل رشته یک مبلغ خوشساختار باشد t را برمیگرداند.
"$100.50" valid-amount? . ! => t
"$100" valid-amount? . ! => t
"$100.5" valid-amount? . ! => f
تابع dollar-amounts را تعریف کنید که هر عددی را که مستقیماً پس از یک $ در خط میآید، به ترتیب، برمیگرداند: فقط ارقام را، بدون $.
"spent $100 and $25 today" dollar-amounts .
! => { "100" "25" }
تابع percentages را تعریف کنید که هر عددی را که بلافاصله بعد از یک % میآید برمیگرداند.
"up 5% then down 12%" percentages .
! => { "5" "12" }
تابع flagged? را تعریف کنید که وقتی خط در هر ترکیبی از حروف بزرگ و کوچک به refund یا chargeback اشاره کند، t را برمیگرداند.
"Issued a REFUND today" flagged? . ! => t
"a normal sale" flagged? . ! => f