Автор Тема: Регулярни изрази част 3  (Прочетена 4542 пъти)

0 Потребители и 1 Гост преглежда(т) тази тема.

Stan

  • Hero Member
  • *****
  • Благодарности
  • -Казани: 27
  • -Получени: 135
  • Публикации: 641
Регулярни изрази част 3
« -: 20 Януари 2012, 16:02:32 »
Здравейте в трети урок за регулярните изрази. В този урок ще се запознаем с последните метасимволи, а в следващия урок ще обогатим знанията си с предефинирани класове,POSIX-класове,някои допълнителни "междусимволни дупки"(тези,които характеризират пространствени граници като "^","$" от първи урок) и символни свойства.

Започваме с метасимволите "(" и ")", които служат за създаването на група. - За какво се използва тя ? - Ами най-общо казано за логическo групиране на части от шаблон, като по този начин можем да контролиране областта на влияние на дадено правило и възможност да достъпваме до него (референция към група или по-точно към това, което е между скобите),както и да задаване по-специални (и не толкова често употребявани) части от шаблон(правила).

Ето един прост пример за контролиране на област на влияние на дадено правило:(вижте в предишния урок, когато говорихме за  метасимвола "|"("или") как се справяхме с разделението на шаблона ни)

Код: PHP
  1. $pattern='/^(a|b)$/'; // по-добро решение от /^a$|^b$/

Резултат със символен низ "a" : a
Резултат със символен низ "ab" : ab

Можем да "кръщаваме",тоест да задаваме имена на различните групи - дефинират се така: (?<name>) или (?'name'), където "name" е името на групата.

Можем също да достъпваме(да правим референции,да вземем частта на съвпадение от шаблона в нея) до дадена група - това става външно или вътрешно(в самия шаблон, за което ще говорим).Можем да направим референция по име, ако имаме такова - именоваме групата (?<name>), а референцията към нея по име или число ще бъде \k<name> ,където "name" е името на групата.Ако не сме задали име то можем да достъпваме до групите по ред на номерата им - имаме две групи (a)(b) за да се обърнем към първата група пишем \1 ,за втората \2 и т.н, ако имаме повече.

Нека дадем един прост пример за референция:

Код: PHP
  1. $pattern='/([0-9]+)=\1/';

Резултат със символен низ "4=4" : 4=4
Резултат със символен низ "30=30" : 30=30
Резултат със символен низ "3=30" : 3=30
Резултат със символен низ "6=8" : 6=8

Ето същия пример(резултата е абсолютно идентичен на горния) само, че сме именовали групата с име "numbers" :

Код: PHP
  1. $pattern='/(?<numbers>[0-9]+)=\k<numbers>/';

Разбира се можем да правим повече от една референция на група :

Код: PHP
  1. $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

Ето още един пример:

Код: PHP
  1. $pattern='/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)\11/';

Резултат със символен низ "regular expressions" : regular expressions

Защо се намери съвпадение точно с "ular express" ? - Ами в случая сме задали правило да се намери съвпадение с 12 символа като 11 от тях са произволни (и са без нов ред) а 12-стият е референция на 11-тата ни група, в която има произволен символ в случая "s".

Нека създадем правило, с което казваме да се намери съвпадение с всеки символ(без нов ред),който се повтаря три пъти,като и самото правило повтаряме три пъти:
Код: PHP
  1. $pattern='/(.)\1{2}(.)\2{2}(.)\3{2}/';

Резултат със символен низ "aaabbbccc" : aaabbbccc
Резултат със символен низ "abcabcabc" : abcabcabc

Можем да създадем група, но без референция към нея. - (?:pattern)

Код: PHP
  1. $pattern='/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(?:.)\11/';

Резултат със символен низ "regular expressions" : regular expressions

За външните референции към групи може да прочетете в допълнението към този урок.

Важно е да се отбележи, че има също и рекурсивни групи.Когато направим референция към една група ние всъщност взимаме вече съвпадналия с шаблона символен низ.Ако искаме да преизпълним наново самата група с даден шаблон тогава използване рекурсия.

Тук е момента да споменем и четири специални кострукции:( положителен и отрицателен look-ahead и look-behind)

Начин на записване на положителен look-ahead(поглед напред): (?=pattern)
Пример:
Код: PHP
  1. $pattern='/username(?=abcd)/';

Резултат със символен низ "usernameabcd": usernameabcd
Резултат със символен низ "username abcd" username abcd

Нека обясним какво прави: първата задача е да се открие съвпадение с шаблона ни извън скобите username (символ по символ),след като енджина, който борави с регулярните изрази намери съвпадение и с последния символ от шаблона, в случая "e"  "поглежда напред"(поглежда пред шаблона username) за шаблона abcd,ако той следва завършва успешно като шаблона abcd не се включва в резултата.Тъй като във втория резултат имаме произволен символ, в случая празно място между username и abcd, няма да се намери съвпадение с abcd при "поглеждане напред",защото енджина е стигнал до символ "е", а след него следва празно място, а не търсеният напред шаблон abcd.

Начин на записване на отрицателен look-ahead: (?!pattern)

Код: PHP
  1. $pattern='/username(?!abcd)/';

Резултата със символен низ "username abcd" : username abcd
Резултата със символен низ "usernameabcd" : usernameabcd

Нещата са почти идентични като по-горе, с разликата, че ако се открие с "поглед напред" шаблона abcd понеже е отрицателен ще завърши с неуспех,и обратно, ако не се открие както е в символен низ "username abcd" ще завърши с успех.

Начин на записване на положителен look-behind(поглед назад): (?<=pattern)

Код: PHP
  1. $pattern='/(?<=username)abcd/';

Резултата със символен низ "usernameabcd": usernameabcd
Резултата със символен низ "username abcd": username abcd

Нека и тук обясним какво прави:първата задача е да се открие съвпадение с шаблона ни извън скобите abcd (символ по символ),след като енджина, който борави с регулярните изрази намери съвпадение и с последния символ от шаблона, в случая "d"  "поглежда назад"(поглежда зад шаблона abcd) за шаблона username,ако той предхожда завършва успешно като шаблона username не се включва в резултата.Тъй като във втория резултат имаме произволен символ, в случая празно място между username и abcd, няма да се намери съвпадение с username при "поглеждане назад",защото предхожда празно място, а не търсеният назад шаблон username.

Начин на записване на отрицателен look-behind: (?<!pattern)

Код: PHP
  1. $pattern='/(?<!username)abcd/';

Резултат със символен низ "username abcd": username abcd
Резултат със символен низ "usernameabcd": usernameabcd

Нещата са почти идентични като по-горе, с разликата, че ако се открие с "поглед назад" шаблона username понеже е отрицателен ще завърши с неуспех,и обратно, ако не се открие както е в символен низ "username abcd" ще завърши с успех.

И накрая искам да поясня, защо шаблоните поместени в lookahead и lookbehind не се включват в резултата.Имаме следния елементарен шаблон:

Код: PHP
  1. $pattern='/regular/';

Резултат със символен низ "regular": regular

Тук енджина, който борави с регулярните изрази сравнява всеки символ от символния низ със символа от шаблона "r" с "r", "e" с "e" и т.н.Когато използваме lookahead и lookbehind ние караме този енджин да "поглежда" временно напред или назад спрямо даден шаблон и затова тези съвпадения с предходни или следващи шаблони не влизат в резултата на съвпадение.Ще дам един лесен пример, за да разберете по-добре какво имам предвид:

Код: PHP
  1. $pattern='/e(?=x)i/';

Резултат със символен низ "exit": exit

Защо не се намери съвпадение с "e" след като следващият символ в символния низ е "x" ? - Ами енджина първо търси съвпадение със символ "e", намира го и вижда, че трябва да "погледне напред", вижда че след символ "e" следва символ "x", което удолетворява положителния поглед напред, но не спира дотук,защото вижда, че има и още един символ от шаблона в случая "i", с който трябва да намери съвпадение, и точно тук се появява проблем.След като е намерил съвпадение с "e" той временно поглежда напред и се връща обратно на следващия символ от символния низ, който е "x" - той не съвпада с "i" и връща неуспех като резултат.

По-описателно обяснение на lookahead и lookbehind може да видите тук.

Ето един пример с използване на положителен look-ahead и look-behind:

Код: PHP
  1. $pattern='/(?<=<b>)[a-z]+(?=<\/b>)/';

Резултата със символен низ "<b>word</b>": <b>word</b>

Този урок може би ще е най-труден за разбиране от начинаещите, така че се упражнявайте.Това е засега.

--> Към последния урок.
« Последна редакция: 09 Август 2012, 15:15:27 от Stan »

Румен Аргиров

  • Jr. Member
  • **
  • Благодарности
  • -Казани: 8
  • -Получени: 2
  • Публикации: 51
Re: Регулярни изрази част 3
« Отговор #1 -: 20 Януари 2012, 20:39:46 »
Браво за труда :) Супер уроци!

jdimov

  • Newbie
  • *
  • Благодарности
  • -Казани: 1
  • -Получени: 0
  • Публикации: 1
Re: Регулярни изрази част 3
« Отговор #2 -: 01 Септември 2013, 09:48:08 »
Браво и благодаря!!! :)

georgirgeorgiev

  • Full Member
  • ***
  • Благодарности
  • -Казани: 77
  • -Получени: 13
  • Публикации: 164
Re: Регулярни изрази част 3
« Отговор #3 -: 25 Април 2015, 14:22:15 »
Благодаря Ви за поредният урок ! Обяснявате много добре. Много описателно и лесно разбираемо.

БОГ да ви благослови.
« Последна редакция: 25 Април 2015, 14:57:00 от georgirgeorgiev »