Ми вже знаємо основи: класи символів, як-от \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
Оскільки $ означає кінець рядка тексту (англ. string), для буквального знака долара пишіть \$.
Межа слова (\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, коли весь рядок тексту (англ. string) є правильно
оформленою сумою.
"$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?, яка повертає t, коли рядок згадує refund або
chargeback у будь-якому поєднанні великих і малих літер.
"Issued a REFUND today" flagged? . ! => t
"a normal sale" flagged? . ! => f
Зареєструйтеся на Exercism, щоб вивчати й опановувати Factor, а також 47 концепцій163 вправи та справжнє наставництво від людей, і все це безкоштовно.