Синтаксис на Regular Expressions

Въведение

Regular Expressions са широко използван метод за задаване на шаблони за търсене на текст. Специални метасимволи позволяват да се укаже например, дали текстът, който се търси, да е в началото или в края на реда, или пък съдържа n повторения на даден символ.

Regular expressions изглеждат доста страшно на пръв поглед, но те наистина са много прости (т.е. обикновено са прости;) ), удобни и мощни средства.

Препоръчвам ви да си поиграете с regular expressions използвайки Windows REStudio – той ще ви помогне да разберете главните концепции. Освен това в него са включени много предварително дефинирани и коментирани изрази

Нека да започнем нашето обучение!

Прости съвпадения

Всеки прост символ съвпада сам със себе си, освен ако не е метасимвол със специално предназначение, описано по-долу.

Последователност от символи съвпада със същата последователност, например изразът bluh ще съвпадне с bluh в изследвания стринг. Засега е просто, а ?

Можете да задавате символи, които нормално принадлежат към групата на метасимволите или escape последователностите, да бъдат интерпретирани като нормален (литерален) символ, като просто поставите символа  \ пред тях. Например ^ е метасимвол, означаващ начало на реда, а \^ определя самия символ ^. \\ определя \ и т.н..

Примери:

  Foobar                открива 'foobar'
  \^FooBarPtr         открива '^FooBarPtr'

Escape последователности

Всеки символ може да бъде задаван и със синтаксис за escape последователност – така, както е в C и Perl: \n определя символа ‘нов ред’; t определя tab; \r - return; \f -  form feed, и т.н. В общия случай може да се използва \xnn, където nn е поредица от шестнадесетични цифри, указващи ASCII-кода на желания символ. Ако искате да зададете символ от таблицата UniCode, може да използвате \x{nnnn}, където nnnn са една или повече шестнадесетични цифри.

 \xnn                    символ с hex код nn
 \x{nnnn}                символ с hex код nnnn (един байт за чист текст и два - за Unicode)
 \t                       tab (HT/TAB), също и  \\x09
 \n                       newline (NL), също и  \\x0a
 \r                      car.return (CR), също и  \\x0d
 \f                       form feed (FF), също и \\x0c
 \a                       alarm (bell) (BEL), също и \\x07
 \e                       escape (ESC), съшо и  \\x1b

Примери:

 foo\\x20bar                открива 'foo bar' (с интервал в средата)
 \\tfoobar                открива 'foobar', предшестван от tab

Клас от символи

Може да зададете клас от символи, като ги затворите в квадратни скоби []. В този случай се проверява за съвпадение на който и да е символ от класа.

Ако първият символ в класа веднага след [ е ^, се проверява за съвпадение на всички символи, които не са от този клас.

Примери:

 foob[aeiou]r   открива 'foobar', 'foober' и т.н., но не 'foobbr', 'foobcr' и т.н.
 foob[^aeiou]r открива 'foobbr', 'foobcr' и т.н., но не 'foobar', 'foober' и т.н.

Вътре в описанието на класа може да се използва символът - за задаване на обхват, например a-z представлява съкратен запис на всички букви от a до z включително.

Ако искате самият символ - да бъде член на класа, просто го поставете в началото или в края на класа, или пред него сложете \. Ако искате и ] да бъде член на класа, поставете го в началото на списъка или пред него сложете \.

Примери:

 [-az]     открива 'a', 'z' и '-'
 [az-]     открива 'a', 'z' и '-'
 [a\-z]     открива 'a', 'z' и '-'
 [a-z]     открива всички 26 малки букви от 'a' до 'z'
 [\n-\x0D] открива което и да е от #10,#11,#12,#13.
 [\d-t]     открива всички цифри, '-' or 't'.
 []-a]     открива произволен символ в обхвата ']'..'a'.

Метасимволи

Метасимволите са специални символи, които са същността на Regular Expressions. Има различни типове метасимволиописани по-долу.

Метасимволи - разделители

 ^                начало на ред
 $                край на ред
 \A                начало на текст
 \Z                край на текст
 .                произволен символ

Примери:

 ^foobar              открива 'foobar' само ако е в началото на реда
 foobar$               открива 'foobar' само ако е в края на реда
 ^foobar$             открива 'foobar' само ако е единствения стринг на реда
 foob.r                открива стрингове като
 'foobar', 'foobbr', 'foob1r' и т.н.

По подразбиране символът ^ съвпада само с началото на обработвания фрагмент от текста, а символът $ - с неговия край (или пред \n в края), което позволява ускоряване на процеса по откриване на съвпадения. Всички символи \n, които се срещат вътре в текстовия блок, не се разпознават от ^ или $.

Ако обаче искате да третирате входния стринг като буфер с много редове текст, при което ^ да съвпада с началото на всеки ред в този буфер (след \n), а $ – с края на всеки ред (преди \n), това може да стане чрез активиране на модификатора /m.

Метасимволите \A и \Z съвпадат с ^ и $, с изключение на това, че при тях няма многократни съвпадения в случай че се използва модификаторът /m, докато в този случай ^ и $ ще откриват всички вътрешни за текста знаци за край на ред.

Метасимволът . по подразбиране съвпада с всеки символ в текста, но ако изключите модификатора /s, то . няма да открива вътрешните за текста знаци за край на ред.

TRegExpr работи със знаците за край на текст според препоръките на www.unicode.org:

^ съвпада с началото на входния стринг, и, ако модификаторът /m е включен, също така със символите, непосредствено следващи всяко срещане на комбинациите \x0D\x0A или \x0A или \x0D (Ако използвате Unicode-версията на TRegExpr, и след \x2028 или  \x2029 или \x0B или \x0C или \x85). Забележете, че няма празен ред в комбинацията \x0D\x0A.

$ съвпада с края на входния стринг, и, ако модификаторът /m  е включен, също така със символите, непосредствено предшестващи всяко срещане на комбинациите \x0D\x0A или \x0A или \x0D (Ако използвате Unicode-версията на TRegExpr, и преди \x2028 или  \x2029 или \x0B или \x0C или \x85). Забележете, че няма празен ред в комбинацията \x0D\x0A.

. съвпада със всеки символ, но ако изключите модификатора /s тогава . не съвпада с \x0D\x0A и \x0A и \x0D (Ако използвате Unicode-версията на TRegExpr, и с \x2028 или  \x2029 или \x0B или \x0C или \x85).

Забележете, че ^.\*$ (шаблон за празен ред) няма да даде съвпадение за празен ред в комбинацията \x0D\x0A, но ще даде такова за комбинацията \x0A\x0D.

Обработката на много редове може да бъде настройвана лесно за Вашите нужди чрез свойствата на TRegExpr LineSeparators и LinePairedSeparator, Може да ползвате само разделителите /n в стил Unix , или \r\n на DOS/Windows, или да ги смесвате (както е описано по-горе и е по подразбиране), или дори да дефинирате собствени разделители!

Метасимволи – предварително дефинирани класове

 \w     буквено-цифров символ (включително `_`)
 \W   небуквено-цифров символ
 \d     цифров символ
 \D     нецифров символ
 \s     интервален символ (същото като [\t\n\r\f])
 \S     неинтервален символ

Може да използвате \w, \d и \s в произволни символни класове.

Примери:

 foob\dr               съвпада със стрингове като 'foob1r', ''foob6r' и т.н., но не с  'foobar', 'foobbr' и т.н.
 foob\[\w\s\]r      съвпада със стрингове като 'foobar', 'foob r', 'foobbr' и т.н., но не с 'foob1r', 'foob=r' и т.н.

TRegExpr използва свойствата SpaceChars и WordChars за дефиниране на символните класове \w, \W, \s, \S, така, че Вие лесно можете да си ги предефинирате.

Метасимволи - итератори

Всяка част от един regular expression може да бъде последвана от дриг вид метасимволи - итератори. Чрез тях може да задавате броя на срещанията на предхождащите ги символ, , метасимвол или подизраз.

 \*     нула или повече пъти ("жаден"), подобно на {0,}
 +   един или повече пъти ("жаден"), подобно на {1,}
 ?   нула или един път ("жаден"), подобно на {0,1}
 {n}   точно n пъти ("жаден")
 {n,}   най-малко n пъти ("жаден")
 {n,m} най-малко n но не повече от m пъти ("жаден")
 \*?     нула или повече пъти ("нежаден"), подобно на {0,}
 +?     един или повече пъти ("нежаден"), подобно на {1,}
 ??     нула или един път ("нежаден"), подобно на {0,1}
 {n}?   точно n пъти ("нежаден")
 {n,}? най-малко n пъти ("нежаден")
 {n,m}? най-малко n но не повече от m пъти ("нежаден")

И така, цифрите във фигурните скоби от типа {n,m} указват минималният брой съвпадения n и максималния m. Формата {n} е еквивалент на {n,n} и осигурява съвпадение точно n пъти. Формата {n,} осигурява съвпадения n или повече пъти. Няма огранияенич за голенимата на n или m, но по-големите числа водят до заемане на повече памет и до забавяне на изчислението на RE.

Ако фигурните скоби се срещнат на друго място, те се третират като обикновени символи.

Примери:

 foob.\*r                                съвпада със стрингове като 'foobar',  'foobalkjdflkj9r' и 'foobr'
 foob.+r                съвпада със стрингове като 'foobar', 'foobalkjdflkj9r' но не 'foobr'
 foob.?r                                съвпада със стрингове като 'foobar', 'foobbr' и 'foobr' но не 'foobalkj9r'
 fooba{2}r                съвпада с 'foobaar'
 fooba{2,}r                съвпада със стрингове като 'foobaar', 'foobaaar', 'foobaaaar' и т.н..
 fooba{2,3}r                съвпада със стрингове като 'foobaar' или 'foobaaar' но не 'foobaaaar'

Малко обяснение на “жаждата”. “Жаден” дава колкото се може повече съвпадения, “нежаден” дава колкото се може по-малко съвпадения. Например, b+ и b\* , приложени към abbbbc връщат bbbb, b+? връща b, b\*? връща празен стринг, b{2,3}? връща bb, b{2,3} връща bbb.

Всички итератори може да бъдат превключени в “нежаден” режим (виж модификатора /g).

Метасимволи - алтернативи

Може да задавате няколко алтернативи за даден израз, използвайки символа | като разделител между тях. Например fee|fie|foe ще съвпадне с която и да е от думите fee, fie, или foe във входния поток (същият резултат ще се получи и с f(e|i|o)e). Първата алтернатива включва всичко от последния разделител ((, [, или началото на RE) до първия |, а последната – всичко от последния | до следващия разделител. Затова е препоръчително да поставяте алтернативите в скоби, за да се избегнат недоразуменията за това къде започват или завършват.

Алтернативите се преглеждат от ляво на дясно, като при това се избира първият подходящ вариант. Това означава, че алтернатовите не е задължително да са “жадни”. Например: ако търсим foo|foot в barefoot, ще имаме съвпадение на алтернативата foo, понеже тя е най-лявата алтернатива, и удовлетворява съвпадението. (Това може и да не изглежда важно, но е – когато искате да вземете текста, отговарящ на съвпадението, използвайки скоби.)

Освен това не забравяйте, че | се интерпретира като литерален символ в квадратните скоби, така, че ако напишете [fee|fie|foe] всъщност това ще отговаря на [feio|].

Примери:

foo(bar|foo) съвпада с 'foobar' или 'foofoo'.

Метасимволи - подизрази

Кръглите скоби ( ... ) може да бъдат използвани и за отделяне на подизрази (след изпълнение на RE имате информация за позицията, дължината и стойността на всеки подизраз, намираща се в променливите MatchPos, MatchLen и свойствата Match на TRegExpr, а също така можете и да ги замествате в шаблонни стрингове с помощта на TRegExpr.Substitute).

Подизразите са номерирани, като номерацията се базира на подреждането отляво надясно на отварящите скоби.

Първият подизраз има номер 1 (целия RE отговаря на номер 0 – по този начин може да го използвате за заместване в TRegExpr.Substitute като $0 или $&).

Примери:

(foobar){8,10}          открива текст, съдържащ 8, 9 или 10 срещания на 'foobar'
foob([0-9]|a+)r      съвпада с 'foob0r','foob1r' , 'foobar', 'foobaar', 'foobaar' etc.

Метасимволи – обратни връзки (backreferences)

Метасимволите от \1 до \9 се интерпретират като обратни връзки. С метасимвола \<n> се задава съответствие с предварително намереният вече подизраз номер <n>.

Примери:

 (.)\1+                          открива напр. 'aaaa' и 'cc'.
 (.+)\1+                        открива напр. 'abab' и '123123'
 (['"]?)(\d+)\1                открива '"13" (в двойни кавички), или '4' (в единични кавички) или 77 (без кавички) и т.н.

Метасимволи – граници на думи (word boundaries)

\b                Match a word boundary
\B                Match a non-(word boundary)

Граница на дума (\b) е мястото между два символа. То има w от едната си страна и \W от другата (независимо от реда им), и представлява въображаемите символи, които са между стринга, определен като \W.

Модификатори

Модификаторите служат за промяна на работата на TRegExpr.

Има много начини за задаване на описаните по-долу модификатори.

Всеки от тези модификатори може да бъде вграден в самия RE чрез конструкцията (?...).

Също така може да присвоявате стойности на свойствата на TRegExpr (на ModifierX например за промяна на /m, или на ModifierStr за промяна на всички модификатори едновременно). Стойностите по подразбиране за всеки нов обект от тип TRegExpr са дефинирани в глобални променливи, например RegExprModifierX дефинира първоначалната стойност на свойството ModifierX на новия обект от тип TRegExpr.

 

i

Проверката става без да се влияе от регистъра на символите (малки/главни букви) (сравнението се базира на инсталирания език на Вашата система). Виж също InvertCase.

m

Възприема входния стринг като съставен от много редове, т.е. ^ и $ съвпадат с разделителите вътре в стринга (виж метасимволи-разделители)

s

Възприема входния стринг като един текстов ред, като метасимволът . съвпада с всеки символ от стринга, включително и с разделителите (виж метасимволи-разделители), с които той нормално не съвпада.

g

Нестандартен модификатор. Изключете го, ако искате всички следващи операции да са в нежаден режим  (по подражбиране е включен). Ако /g е изключен, то + работи като +?, * като *? и т.н.

x

Разширен синтаксис – допуска празни символи и коментарии за по-ясно оформяне на RE (виж обяснението по-долу).

r

Нестандартен модификатор. Ако той е включен, в диапазона [а-я] ще се включи и символа ё, [А-Я] ще включва Ё, и [а-Я] ще включва всички символи от руската азбука (с българската азбука този проблем не съществува).

По подразбиране този модификатор е включен, а ако това не ви харесва, изключете го посредством глобалната променлива RegExprModifierR.

Модификаторът /x се нуждае от малко повече обяснение. Той казва на TRegExpr да игнорира интервалите, които не са нито предшествани от ‘', нито са в символен клас. Може да го използвате, за да разделите RE на (малко) по-разбираеми части. Символът # се третира като метасимвол, след който следва коментар, например:

(
(abc) # Коментар 1
  |    # Можете да използвате интервали за разделители в RE - TRegExpr ги игнорира
(efg) # Коментар 2
)

Това също означава, че ако желаете да имате истински интервали или символи # във Вашия шаблон (извън символен клас, където те не се влияят от /x), ще трябва или да ги въвеждате с escape-последователности, или да ги кодирате с осмични или шестнадесетични последователности. Като цяло обаче тези елементи правят RE доста по-удобни за четене.

Perl-разширения

(?imsxr-imsxr)

Може да го използвате вътре в RE за временно включване или изключване на модификаторите. Ако тази конструкция се срещне в подизраз, ря влияе само на него.

Примери:

 (?i)Saint-Petersburg                      открива 'Saint-petersburg' и 'Saint-Petersburg'
 (?i)Saint-(?-i)Petersburg                открива 'Saint-Petersburg' но не 'Saint-petersburg'
 (?i)(Saint-)?Petersburg                  открива 'Saint-petersburg' и 'saint-petersburg'
 ((?i)Saint-)?Petersburg                  открива 'saint-Petersburg', но не 'saint-petersburg'

(?#text)

Коментар, текстът ‘text’ се игнорира. Обърнете внимание, че TRegExpr счита за край на коментара първият срещнат символ ), затова няма възможност да използвате този символ като част от коментара.

Засега не забравяйте да прочетете FAQ (по-специално въпросът за нежадната оптимизация).