Здравейте в трети урок за регулярните изрази. В този урок ще се запознаем с последните метасимволи, а в следващия урок ще обогатим знанията си с предефинирани класове,POSIX-класове,някои допълнителни "междусимволни дупки"(тези,които характеризират пространствени граници като "^","$" от първи урок) и символни свойства.
Започваме с метасимволите "
(" и "
)", които служат за създаването на група. - За какво се използва тя ? - Ами най-общо казано за логическo групиране на части от шаблон, като по този начин можем да контролиране областта на влияние на дадено правило и възможност да достъпваме до него (референция към група или по-точно към това, което е между скобите),както и да задаване по-специални (и не толкова често употребявани) части от шаблон(правила).
Ето един прост пример за контролиране на област на влияние на дадено правило:(вижте в предишния урок, когато говорихме за метасимвола "|"("или") как се справяхме с разделението на шаблона ни)
$pattern='/^(a|b)$/'; // по-добро решение от /^a$|^b$/
Резултат със символен низ "a" :
aРезултат със символен низ "ab" : ab
Можем да "кръщаваме",тоест да задаваме имена на различните групи - дефинират се така:
(?<name>) или
(?'name'), където "name" е името на групата.
Можем също да достъпваме(да правим референции,да вземем частта на съвпадение от шаблона в нея) до дадена група - това става външно или вътрешно(в самия шаблон, за което ще говорим).Можем да направим референция по име, ако имаме такова - именоваме групата (?<name>), а референцията към нея по име или число ще бъде
\k<name> ,където "name" е името на групата.Ако не сме задали име то можем да достъпваме до групите по ред на номерата им - имаме две групи
(a)(b) за да се обърнем към първата група пишем
\1 ,за втората
\2 и т.н, ако имаме повече.
Нека дадем един прост пример за референция:
$pattern='/([0-9]+)=\1/';
Резултат със символен низ "4=4" :
4=4Резултат със символен низ "30=30" :
30=30Резултат със символен низ "3=30" :
3=30
Резултат със символен низ "6=8" : 6=8
Ето същия пример(резултата е абсолютно идентичен на горния) само, че сме именовали групата с име "numbers" :
$pattern='/(?<numbers>[0-9]+)=\k<numbers>/';
Разбира се можем да правим повече от една референция на група :
$pattern='/double number (?<numbers>[0-9]+) is \k<numbers>\k<numbers>/';
Резултат със символен низ "double number 6 is 66" :
double number 6 is 66Резултат със символен низ "double number 7 is 74" : double number 7 is 74
Ето още един пример:
$pattern='/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)\11/';
Резултат със символен низ "regular expressions" : reg
ular expressions
Защо се намери съвпадение точно с "ular express" ? - Ами в случая сме задали правило да се намери съвпадение с 12 символа като 11 от тях са произволни (и са без нов ред) а 12-стият е референция на 11-тата ни група, в която има произволен символ в случая "s".
Нека създадем правило, с което казваме да се намери съвпадение с всеки символ(без нов ред),който се повтаря три пъти,като и самото правило повтаряме три пъти:
$pattern='/(.)\1{2}(.)\2{2}(.)\3{2}/';
Резултат със символен низ "aaabbbccc" :
aaabbbcccРезултат със символен низ "abcabcabc" : abcabcabc
Можем да създадем група, но без референция към нея. -
(?:pattern)$pattern='/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(?:.)\11/';
Резултат със символен низ "regular expressions" : regular expressions
За външните референции към групи може да прочетете в
допълнението към този урок.Важно е да се отбележи, че има също и рекурсивни групи.Когато направим референция към една група ние всъщност взимаме вече съвпадналия с шаблона символен низ.Ако искаме да преизпълним наново самата група с даден шаблон тогава използване рекурсия.
Тук е момента да споменем и четири специални кострукции:( положителен и отрицателен look-ahead и look-behind)
Начин на записване на положителен look-ahead(поглед напред):
(?=pattern)Пример:
$pattern='/username(?=abcd)/';
Резултат със символен низ "usernameabcd":
usernameabcd
Резултат със символен низ "username abcd" username abcd
Нека обясним какво прави: първата задача е да се открие съвпадение с шаблона ни извън скобите
username (символ по символ),след като енджина, който борави с регулярните изрази намери съвпадение и с последния символ от шаблона, в случая "
e" "поглежда напред"(поглежда пред шаблона
username) за шаблона
abcd,ако той следва завършва успешно като шаблона
abcd не се включва в резултата.Тъй като във втория резултат имаме произволен символ, в случая празно място между
username и
abcd, няма да се намери съвпадение с
abcd при "поглеждане напред",защото енджина е стигнал до символ "
е", а след него следва празно място, а не търсеният напред шаблон
abcd.
Начин на записване на отрицателен look-ahead:
(?!pattern)$pattern='/username(?!abcd)/';
Резултата със символен низ "username abcd" :
username abcd
Резултата със символен низ "usernameabcd" : usernameabcd
Нещата са почти идентични като по-горе, с разликата, че ако се открие с "поглед напред" шаблона
abcd понеже е
отрицателен ще завърши с неуспех,и обратно, ако не се открие както е в символен низ "username abcd" ще завърши с успех.
Начин на записване на положителен look-behind(поглед назад):
(?<=pattern)$pattern='/(?<=username)abcd/';
Резултата със символен низ "usernameabcd": username
abcdРезултата със символен низ "username abcd": username abcd
Нека и тук обясним какво прави:първата задача е да се открие съвпадение с шаблона ни извън скобите
abcd (символ по символ),след като енджина, който борави с регулярните изрази намери съвпадение и с последния символ от шаблона, в случая "
d" "поглежда назад"(поглежда зад шаблона
abcd) за шаблона
username,ако той предхожда завършва успешно като шаблона
username не се включва в резултата.Тъй като във втория резултат имаме произволен символ, в случая празно място между
username и
abcd, няма да се намери съвпадение с
username при "поглеждане назад",защото предхожда празно място, а не търсеният назад шаблон
username.
Начин на записване на отрицателен look-behind:
(?<!pattern)$pattern='/(?<!username)abcd/';
Резултат със символен низ "username abcd": username
abcdРезултат със символен низ "usernameabcd": usernameabcd
Нещата са почти идентични като по-горе, с разликата, че ако се открие с "поглед назад" шаблона
username понеже е
отрицателен ще завърши с неуспех,и обратно, ако не се открие както е в символен низ "username abcd" ще завърши с успех.
И накрая искам да поясня, защо шаблоните поместени в lookahead и lookbehind не се включват в резултата.Имаме следния елементарен шаблон:
Резултат със символен низ "regular":
regularТук енджина, който борави с регулярните изрази сравнява всеки символ от символния низ със символа от шаблона "
r" с "
r", "
e" с "
e" и т.н.Когато използваме lookahead и lookbehind ние караме този енджин да "поглежда"
временно напред или назад спрямо даден шаблон и затова тези съвпадения с предходни или следващи шаблони не влизат в резултата на съвпадение.Ще дам един лесен пример, за да разберете по-добре какво имам предвид:
Резултат със символен низ "exit": exit
Защо не се намери съвпадение с "
e" след като следващият символ в символния низ е "
x" ? - Ами енджина първо търси съвпадение със символ "
e", намира го и вижда, че трябва да "погледне напред", вижда че след символ "
e" следва символ "
x", което удолетворява положителния поглед напред, но не спира дотук,защото вижда, че има и още един символ от шаблона в случая "
i", с който трябва да намери съвпадение, и точно тук се появява проблем.След като е намерил съвпадение с "
e" той временно поглежда напред и се връща обратно на следващия символ от символния низ, който е "
x" - той не съвпада с "
i" и връща неуспех като резултат.
По-описателно обяснение на lookahead и lookbehind може да видите
тук.
Ето един пример с използване на положителен look-ahead и look-behind:
$pattern='/(?<=<b>)[a-z]+(?=<\/b>)/';
Резултата със символен низ "<b>word</b>": <b>
word</b>
Този урок може би ще е най-труден за разбиране от начинаещите, така че се упражнявайте.Това е засега.
--> Към последния урок.