Кратко съдържание


Най-важното за работата с регулярни изрази



страница71/73
Дата21.07.2018
Размер9.03 Mb.
#76887
1   ...   65   66   67   68   69   70   71   72   73

Най-важното за работата с регулярни изрази


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

Шаблонът не е за съвпадение с целия низ


Когато търсим съвпадение по даден шаблон, ние търсим произволен подниз на нашия низ, който да отговаря на шаблона. В текста може да има много такива поднизове. Шаблонът не описва целия текст (освен ако изрично не укажем това), а описва поредица символи, която се търси в текста. Например шаблонът "бира" има две съвпадения в низа "Разбирам от бира" – едното е част от думата "разбирам", а другото е самата дума "бира".

Съвпаденията се откриват в реда на срещане


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

Търсенето приключва, когато се открие съвпадение


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

Търсенето продължава от последното съвпадение


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

Това е важна подробност, която трябва добре да се запомни, защото често е причина за объркване! За да я изясним, ще разгледаме примерния шаблон "(бира!){2}". С този шаблон търсим две последователни повто­рения на подниза "бира!", тоест на практика търсим "бира!бира!". Нека да го приложим към примерния низ "бира!бира!бира!". В текстa реално има две срещания на нашия шаблон – едното започва от позиция 0 ("бира!бира!бира!"), а другото – от позиция 5 ("бира!бира!бира!").



С търсенето обаче не можем да получим и двата резултата. При първия опит ще получим по-левия подниз (този от позиция 0). Ако сега търсим отново, то новото търсене започва от позиция 10 (това е първата позиция след края на намереното съвпадение), а оттам до края на низа вече има само едно "бира!" и няма да имаме ново съвпадение.

Регулярният израз търси за всички възможности подред


Това е основният принцип, който описва работата на машината за регу­лярните изрази. При всеки следващ символ тя опитва подред всички въз­можности за намиране на съвпадение от тази позиция и връща първото успешно или продължава нататък. Понякога това може да доведе до търсене с връщане назад (backtracking), за което ще стане дума по-късно.

Основни метасимволи


Сега ще разгледаме най-често срещаните метасимволи в езика на регу­лярните изрази. За човек, който никога не се е сблъсквал с подобна мате­рия, долните редове може да изглеждат неразбираеми на първо четене. Всъщност обаче е необходима единствено малко практика и разучаване на примерите, за да се овладее боравенето с регулярните изрази. Впос­ледствие следващите редове могат да служат и за справочник, към който да се прибягва в случай на нужда.

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


Това са метасимволи, които означават цяло множество от обикновени символи. Срещането на който и да е символ от множеството (или класа) на съответното място в низа се счита за съвпадение. Използват се след­ните означения за различните видове класове от символи:

Точка


Специалният символ . обозначава класа от всички символи, с изключение на символа \n за нов ред (в Windows това е \r\n). Ако е включена опцията Singleline, точката обозначава и символа за нов ред (за опции ще стане дума по-късно в темата). Като пример за този метасимвол, да разгледаме шаблона "би.а" и няколко низа, към които да го приложим:

Шаблон: би.а

бира – има съвпадение "бира"

бива – има съвпадение "бива"

би+а – има съвпадение "би+а"

бивна – няма съвпадение (между "би" и "а" има два символа, а не един)

Квадратни скоби (класове с изброяване)


Конструкцията [редица_символи] е метасимвол, обозначаващ класа от всички обикновени символи, изброени в редицата. Символите се изброя­ват без никакви разделители между тях. Тук има няколко варианта. Еди­ният е простото изброяване на символи от вида [символи]. Това е стан­дартният вид на конструкцията, който вече обяснихме:

Шаблон: би[вр]а

разбира – има съвпадение "бира"

не бива – има съвпадение "бива"

биха ни – няма съвпадение (между "би" и "а" трябва "р" или "в")

Обърнете внимание, че редът на символите в квадратните скоби значение за реда на намерените съвпадения – важен е редът на срещане в низа. Съвпадението с "бира" е първо в "Тази бира я бива", въпреки че в шаблона "в" стои преди "р" в квадратните скоби.

Отрицание на клас


Това е конструкцията [^символи]. Символът ^, поставен веднага след отварящата квадратна скоба, означава, че се търсят съвпадения с всички символи, които НЕ влизат в класа на изброяваните. Важно е да се запомни, че символът ^ има специално значение, единствено когато е пос­тавен веднага след отварящата квадратна скоба, иначе се третира като обикновен символ:

Шаблон: би[^вр]а

разбира – няма съвпадение (между "би" и "а" не трябва да има "р" и "в")

биха ни – има съвпадение "биха"
Шаблон: би[в^р]а

разбира – има съвпадение "бира"

биха ни – няма съвпадение (между "би" и "а" трябва "р", "^" или "в")

би^а – има съвпадение "би^а"

Изброяване с диапазон


Можем да използваме и следния вид клас от символи: [символА-символB]. Този вариант на конструкцията с квадратните скоби търси съвпадения с всички символи, намиращи се в затворения интервал между символA и символB в кодовата таблица. Подобно на ^ символът има специално зна­чение, само ако е между други символи, а не в началото и в края.

Шаблон: би[б-п]а

разбира – няма съвпадение ("р" не е в интервала "б-п")

не бива – има съвпадение "бива"
Шаблон: би[-бп]а

не бива – няма съвпадение (между "би" и "а" трябва "б", "п" или "-")

би-а – има съвпадение "би-а"

Ще отбележим, че трите вида изброяване могат свободно да се комби­нират – например "[^a-zA-Z01]" намира съвпадение с всеки символ, кой­то не е малка или голяма латинска буква, нито цифрите 0 или 1.

Специалните символи в конструкцията за клас


Важно е също да отбележим, че с няколко изключения всички метасим­воли губят смисъла си на метасимволи, ако се използват при изброяване на клас от символи. Например символът точка в израза "te[s.]t" се прие­ма за литерал и този шаблон се удовлетворява от "test" и "te.t", но не и от "teat" или какъвто и да е подобен низ с трети символ, различен от . и s. Изключенията са познатите вече символи:

  • ^ има специално значение след символа [.

  • - има специално значение навсякъде освен след [ или [^ и преди ].

  • ] има специално значение (край на изброяването) навсякъде освен след [ или [^ (празни квадратни скоби водят до грешка).

  • \ винаги има специално значение на escaping character и за да го използваме като литерал, ни трябва \\.

Предефинирани класове от символи


Няколко специални метасимвола обозначават предварително фиксирани класове, чиято употреба често пъти е удобна. Ще ги разгледаме един по един, а като общо правило може да се запомни, че метасимволите за отрицание (т.е. непринадлежност към даден предефиниран клас) са същите като съответните за принадлежност към дадения клас, но с главна буква:

  • \w – Означава всички т. нар. "alphanumeric characters" – букви, цифри и знака за подчертаване. В общия случай (с употреба на Unicode), това включва букви и цифри от всички използвани в Unicode азбуки. Ако е включена опцията ECMAScript, \w е еквива­лентен само на [A-Za-z_0-9].

  • \W – Означава всички символи, които не принадлежат на горния клас.

  • \s – Означава всички "whitespace characters", т.е. празно прост­ранство – интервали, табулации, символа за нов ред и т.н.

  • \S – Означава всички символи, които не са празно пространство.

  • \d – Означава всички десетични цифри (от Unicode езиците).

  • \D – Всички символи, които не са десетични цифри.

Особено внимание при тези метасимволи трябва да се отдели на символа \ пред тях, което налага някои особености при escaping в C#, за които вече споменахме. Често срещани грешки стават и при използването на предефинираните класове в конструкцията с квадратните скоби. Това може да се види и в примерите, които следват:

Шаблон: \d\w\w\s\w\w\w\w\W

4та бира! – има съвпадение "4та бира!"

5те пици, 3те тоника – има съвпадение "5те пици," ("3те тоник" не е съвпадение, защото "к" не влиза в класа \W)

три часа и половина – няма съвпадение (няма цифра отпред)
Шаблон: [\D].[\w] (както и [^\d].[\w])

5та – няма съвпадение (първият символ трябва да не е цифра)

т.5 – има съвпадение "т.5" (но точката си остава специален символ!)
Шаблон: [^\d\s]

a – има съвпадение "а" (един символ, който не е цифра и whitespace)

4няма съвпадение
Шаблон: [\D\S]

а – има съвпадение "a"

4 – има съвпадение "4" (търсим символ, който не е цифра, или символ, който не е празно пространство – "4" не е празно пространство)

Както се вижда, [\D] е същото като [^\d], но [\D\S] не е същото като [^\d\s]. В подобни случаи трябва да внимаваме какво точно имаме пред­вид в шаблона.

Метасимволи за количество


Едни от най-често използваните метасимволи са тези за количество повто­рения на даден подниз или символ в шаблона (quantifier metacharac­ters). С няколко такива метасимвола вече се сблъскахме в горните при­мери, а сега ще ги разгледаме по-подробно.

Символът * – нула или повече повторения


С * означаваме 0 или повече повторения на символа (или метасимвол, включително клас от символи), предхождащ знака *:

Шаблон: бира*

разбирам – има съвпадение "бира"

бираааааа! – има съвпадение "бираааааа"

биррррра! – има съвпадение "бир" (* важи само за символа "a")

бирено коремче – има съвпадение "бир" (0 повторения на "а")

Ако преди звездичката има клас от символи, то търсим поредица от последователни срещания на символи от този клас. Не се изисква обаче това да са повторения един и същи символ от този клас. Ще напомним и че звездичката вътре в квадратните скоби губи специалното си значение:

Шаблон: бa[ла]*йка

балалайка – има съвпадение "балалайка"

баллалаалайка – има съвпадение "баллалаалайка"

баалайка – има съвпадение "баалайка"
Шаблон: ба[л*а*]йка

балалайка – няма съвпадение (трябва "л", "а" или "*" по средата)

байка – няма съвпадение

ба*йка – има съвпадение "байка"

Горният пример показва, че не можем да използваме квадратните скоби и звездичката, за да означим повторение на някой от символите в класа на квадратните скоби. Ако искаме няколко пъти "a" или няколко пъти "б", не ни върши работа нито "[a*б*]" (търси само веднъж "а", "б" или "*"), нито "[aб]*" (което пък намира например "аббаб"). В такива случаи ще ни трябва специалният символ за алтернативен избор, който също ще разгле­даме малко по-късно. Ако пък искаме да се повтаря точно определена редица символи, можем да използваме метасимвола за група и да оградим редицата в скоби: (редица)*. Това групиране има и други приложения, за които подробно ще говорим по-късно, но засега е важно да знаем, че то ни позволява да отделяме логически части от шаблона и да прилагаме някой специален метасимвол върху цели такива части:

Шаблон: ба(ла)*йка

байка – има съвпадение "байка"

балалайка – има съвпадение "балалайка"

баллалайка – няма съвпадение ("лла" го няма в шаблона)

Символът + – едно или повече повторения


Този метасимвол е идентичен със символа *, като единствената разлика между двата, е, че + изисква задължително поне едно повторение на конструкцията, за която се отнася – т.е. той съвпада 1 или повече пов­торения.

Шаблон: бира+

бираааааа! – има съвпадение "бираааааа"

бирено коремче – няма съвпадение (трябва поне едно "а" след "бир")

Символът ? – нула или едно повторения


Въпросителният знак е метасимвол, който означава 0 или 1 повторения. Обикновено се използва за някаква незадължителна конструкция в шаб­лона, която или се среща само веднъж, или изобщо не се среща. В при­мера отново използваме ограждането със скоби за цяла група.

Шаблон: няма( бира)?

няма бира – има съвпадение "няма бира"

бира няма – има съвпадение "няма"

Метасимволи за точен брой повторения


Ако искаме да укажем с по-голяма точност броя на последователните сре­щания на даден символ или конструкция, можем да използваме специал­ните символи за точен брой повторения. С {n} указваме, че прехож­дащият (мета)символ ще се повтаря точно n пъти. {n,} означава поне n повторения, а {n,m} е за поне n, но не повече от m последова­телни срещания на дадената конструкция:

Шаблон: бир{2,3}а*

разбирам – няма съвпадение ("р" трябва да се среща поне 2 пъти)

биррааааа! – има съвпадение "бирраааааа"

биррррра! – има съвпадение "биррр" ("р" се среща 4 пъти, конструкцията позволява до 3 и взима максималния брой)

Лесно можем да забележим, че познатите ни *, + и ? могат да се представят чрез символите за точен брой повторения, както следва: * като {0,}, + като {1,} и ? като {0,1}.

"Мързеливи" метасимволи за количество


По-рано казахме, че регулярният израз винаги връща най-лявото съвпа­дение. С въвеждането на метасимволите за количество обаче виждаме, че има възможност две съвпадения да започват от една и съща позиция и просто да са различно дълги. Кое се избира тогава?

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


Пример за "лакомо" съвпадение


Регулярен израз – "\w*".

Низ, в който ще търсим – "Проба".

Ето какво ще получим в резултат:

Как можем да разтълкуваме полученото? Да си припомним какво казахме за начина на откриване на съвпадения по-рано в секцията за литералите. Машината за регулярните изрази започва търсенето си от позиция 0. Най-дългият низ, който може да се получи в съответствие с шаблона, е целият низ "Проба", което и виждаме като първи резултат.

Понеже търсенето продължава оттам, откъдето завършва предното съв­падение, то остава да се търси само в позиция 5 – където вече свършва низът. Сега началото и краят на всяко възможно съвпадение ще са на една и съща позиция, и следователно тук машината може да намери съв­падения единствено с празния низ. Празният низ обаче също отговаря на шаблона, ето защо се открива като съвпадение и го виждаме в резултата.

Ако бяхме използвали шаблона "\w+", то празният низ нямаше да бъде разпознат, защото не съдържа нито един alphanumeric символ, и "Проба" щеше да е единственият намерен подниз.

Това, което трябва да запомним, е че търсене не се провежда от всяка възможна позиция, а се избира едно съвпадение (при "лакомите" метасим­воли за количество това е най-дългото възможно съвпадение) и търсенето продължава от неговия край. Ето защо понякога може да се случи да изгубим резултат, който ни интересува.

Нежелано "лакомо" съвпадение


Как можем да променим този механизъм на търсене, ако искаме да наме­рим низ, който не е максималното съвпадение? Единият начин е да изпол­зваме метасимволите за точен брой повторения. Този подход обаче не винаги е в състояние да реши проблема.

Да разгледаме следния пример – искаме да извадим HTML тагове от някакъв HTML документ. По-конкретно, нека имаме низа "


This is a paragraph tag
". Нашата задача е да открием таговете
и
. Лесно се вижда, че това не може да стане с израза <.+>, за който може би бихме се сетили първо. Понеже метасимволът + е "лаком", търсенето по този израз ще намери първо като съвпадение направо целия низ и ние няма да извлечем желания резултат.

Не можем да приложим и символите за точен брой повторения – за долна граница можем да сложим 1, но не знаем каква горна граница да зададем. Тук тагът е еднобуквен, но в HTML може да има много разно­образни по дължина тагове. Дори да зададем някаква надвишаваща и най-дългия таг горна граница, това не ни води до вярното съвпадение, защото в общия случай пак можем да съвпаднем и текста между отварящия и затварящия таг, а освен това символът > може и отделно да присъства в този текст.


"Мързеливо" съвпадение


Решението на проблема тук е в конструкцията за т. нар. "мързеливо" съв­падение. С добавяне на символа ? след всеки от метасимволите за коли­чество, ние принуждаваме машината на регулярните изрази да приеме първото възможно (съответно и най-кратко) съвпадение. Да използваме тази конструкция в нашия пример – да трансформираме израза в <.+?>. Сега вече получаваме точно търсения резултат - "
" и "
".

"Мързеливото" съвпадане може да се прилага за всички споменати спе­циални символи за количество – *?, +?, ??, {n}? и т.н. Разгледани отдел­но, изглежда, че тези конструкции просто могат да се заместят с долните граници за брой повторения на съответните метасимволи (0 за * и ?, 1 за + и т.н.), но както видяхме, това не е точно така. Ситуацията се променя, когато мързеливите метасимволи са последвани от друга част от шаблона, която трябва да съвпадне. Тогава минималният брой повторения са повто­ренията в низа до достигането на тази друга част от шаблона.


Метасимволи за местоположение


Метасимволите за местоположение (zero-width assertions или anchors) се различават от вече разгледаните типове, защото не се използват за съвпадане на символи в текста, а за съвпадане с позиция в текста. Те обикновено се използват, за да укажат, че дадена поредица от символи трябва да се намира на някакво точно определено място в текста. Съвпаденията, които намират, са с нулева дължина и не карат машината на регулярните изрази да премине на следващия символ. Какво означава това на практика, ще разберем след като разгледаме примерите за отдел­ните метасимволи.

Символът ^


С помощта на този символ можем да поискаме съвпадението да се намира в началото на низа. Символът ^ е шаблон за позицията преди първия символ.

Шаблон: ^бира

разбирам – няма съвпадение (поднизът "бира" не е в началото)

бираааааа! – има съвпадение "бира"

бирено коремче – няма съвпадение (няма "бира" в началото)

Символът $


Аналогично, символът $ изисква съвпадение с края на низа (позицията след последния символ). Има едно изключение от това правило и то е ако низът завършва със символа за нов ред \n. Тогава $ намира съвпадение и с позицията преди този символ:

Шаблон: бира$

разбирам – няма съвпадение (поднизът "бира" не е в края)

хубава бира – има съвпадение "бира"

скарата гълта много бира\n – пак има съвпадение "бира" (има само "\n" до края)

Символите ^ и $ в многоредов режим


Често когато четем например текст от файл, където има много нови редове, се интересуваме от позицията на търсения низ в рамките на реда, а не на целия текст. Синтаксисът на регулярните изрази позволява символите ^ и $ да означават съответно началната и крайната позиция не само на целия низ, в който търсим, а и на даден ред от него. Тогава:

Шаблон: ^бира

лято е.\n

бирата е студена – има съвпадение "бира"

лято е,\n

но бирата е студена – няма съвпадение ("бира" не е в начало на ред)

Шаблон: \w{4}\?$



какво лято?\n

каква бира? – две съвпадения "лято" и "бира" (4 alphanumeric символа и въпросителна на края на ред)

За да използваме тази функционалност на символите ^ и $, трябва първо да активираме опцията Multiline при търсенето. Как става това, ще разберем в частта за опциите, по-нататък в темата. Да обърнем внимание, че тази опция не е включена по подразбиране.

Употреба на символите за край и начало


Основното приложение на тези метасимволи е при валидация на потреби­телски вход, както ще се убедим не след дълго. Поставяйки нашия шаблон между символите за начална и крайна позиция, ние на практика задължа­ваме целия текст да отговаря на шаблона. Ако не го направим, регуляр­ният израз търси съвпадение в произволна част от текста и може да се окаже така, че само тази част отговаря на валидиращите условия, което не е коректно решение на задачата за валидация.

Разбира се, метасимволите ^ и $ могат да се употребяват и за много други цели. Може да ни се налага например да търсим първите думи на редо­вете в дълъг текст и да променяме първата им буква на главна.

Един друг пример използва важна особеност на тези символи – те могат да намират като съвпадение празния низ (това е вярно още единствено за конструк­циите .* и .?). Това понякога не е удобно и трябва да внима­ваме, но ако искаме например да добавим нещо в началото на всеки ред (подобно на символите ">", които някои уеб-базирани системи за изпра­щане на поща слагат автоматично при цитиране на писмото в отговора), можем да се възползваме именно от съвпадането на празния низ (ще използваме метода Replace(…), за който ще стане дума по-късно):

Regex.Replace(text, @"^", ">", RegexOptions.Multiline);

Някои особености


Добре е да се запомни, че символите $ и ^ трябва да бъдат escape-вани навсякъде в регулярния израз, ако искаме да ги представим като лите­рали. На пръв поглед изглежда, че те имат смисъл само в началото и в края на шаблона и ако не са на тези позиции, няма нужда да се третират като специални, но това не е така заради функцията Multiline.

Една последна важна бележка се отнася до символа за край на ред/текст $. Трябва да се внимава с позицията, на която той намира съвпадение, защото тя не е валидна позиция в низа. Така че ако се опитаме да изпол­зваме позицията на съвпадението като индекс върху низа, ще се сблъс­каме с изключение, защото сме излезли извън границите му. Следният пример ще доведе до грешка:



string text = "тестов низ";

string pattern = "$";

Match match = Regex.Match(text, pattern);

char matchIndex = text[match.Index];



Още не сме обяснили класовете за работа регулярни изрази в .NET, но се вижда, че се опитваме да осъществим достъп до несъществуващ елемент на низа. Подобно нещо може да се получи и с изразите "^" и "^$", ако сме в многоредов режим и низът завършва със символа за нов ред (да си спомним, че тогава символът ^ намира съвпадение след всяко \n).

Символите \A, \Z и \z


Символите \A и \Z напълно отговарят по функционалност съответно на символите ^ и $, с тази разлика, че не позволяват многоредовото съвпа­дане. Независимо дали е активирана опцията Multiline, те винаги откри­ват за съвпадение единствено началото и края на целия низ, а не на всеки ред.

Символът \z се различава от \Z и от $ по това, че той не открива съвпадение преди "\n" на края на низа:



Шаблон: бира\Z

скарата гълта много бира\n – има съвпадение "бира"
Шаблон: бира\z

скарата гълта много бира\n – няма съвпадение

Символът \b – граници на думи


Специалният символ \b се използва за указване на позиция "на границата на дума". По-точно той е шаблон за съвпадение с нулева дължина, който намира съвпадения в позициите между символ от класа \w (alphanumeric символи, които най-общо съставят думите) и символ от класа \W. Съвпа­дение с \b има и в началото или края на низа, ако той започва, респек­тивно завършва с alphanumeric символ.

Малко примери – нека търсим с израза "\bбир\w*" в низа "бирата вече не е топла, набираме доброволци да търсят студена бира". Изходът тук е следният:



Какво става при търсенето в този пример? Машината започва да търси съвпадения с първия символ от регулярния израз, който е \b. Тъй като в низа първият символ е alphanumeric, то началото му се счита за успешно съвпадение. Нататък машината трябва да провери за подниз "бир" и остатък от прилежащата дума (нула или повече word characters). Така първата дума – "бирата" – се хваща от шаблона. Това става и при послед­ната дума, където метасимволът \b съвпада с позицията след интервала и преди "б" (защото единият е от клас \w, а другият – от \W). Думата "набираме" обаче не е валиден екземпляр на шаблона, защото позицията преди подниза "бир" не се намира на границата на дума.


Символът \B


Символът \B намира съвпадение навсякъде, където \b не намира, тоест на позиции между два символа от клас \w или между два символа от клас \W. Началото и краят на низ, започващ (респективно свършващ) със символ, който не е alphanumeric/word character, също са съвпадения за този мета­символ.

Метасимволът за избор |


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

Трябва да отбележим, че символът | има най-нисък приоритет при регу­лярните изрази и търси съвпадение с целия подниз-шаблон от лявата си страна и целия подниз от дясната. Ако искаме да променим това пове­дение, трябва да използваме групиране (което ще обясним по-късно), за да ограничим видимостта на метасимвола. При групиране, символът | действа в границите на групата.



Шаблон: бира|биричка

хубава бира – има съвпадение "бира"

биричката става – има съвпадение "биричка"

бирено коремче – няма съвпадение

бирбиричка – няма съвпадение (| не е само между "а" и "б")
Шаблон: вишн(а|я)та

вишната – има съвпадение "вишната"

ята – няма съвпадение (| е само между "а" и "я")

Повече възможности


Разбира се, метасимволът за алтернативен избор може да се използва последователно нееднократно, за да означим повече от две възможности.

Шаблон: вишн(а|я|и|овка)

вишничка – има съвпадение "вишни"

вишновка – има съвпадение "вишновка"
Шаблон: \bвишн(а|я|и|овка)\b

вишничка – няма съвпадение (след второто "и" трябва граница на дума)

вишновка – има съвпадение "вишновка"

Както се вижда от първия пример, трябва да внимаваме да не би да търсим погрешно за части от думи ("вишни" във "вишничка"), а всъщност да искаме цели думи (само "вишни"). Решението се вижда във втория пример – добавяме символа за граница на дума.

Можем да забележим, че с единични символи конструкцията "(а|б|в|г)" не е по-различна от "[абвг]", но силата на алтернативния избор е, че можем да описваме много по-разнообразни възможности от единичен символ или клас от символи. Можем също да влагаме вътрешни шаблони като избори (чрез групиране, за което ще разкажем по-късно) и т.н.


По-обстоен пример с разгледаните метасимволи


Нека сега се опитаме да обединим казаното дотук в един по-голям регу­лярен израз и да разгледаме прилагането му върху някой низ:

Шаблон: ^.+?\s\w*((.е)+н)?\s+\w*\s.+?(\d){2,5}.*?[БИРА]+

Низ: това изречение има от две до пет цифри тук: 346; и може би РИБА, БИРА или БАР..



Да започнем да прилагаме шаблона. Веднага виждаме, че съвпадението с шаблона трябва да започва от началото на низа заради символа ^. По-нататък машината започва да прилага възможните варианти на шаблона:

Шаблон: .+?

Съвпада с: "това"

Коментар: Използваме мързеливото съвпадане с точка. Ако не направим това, символът + става "лаком" и ще поеме сам целия низ, защото точ­ката отговаря на всеки символ от него
Шаблон: \s

Съвпада с: " "


Шаблон: \w*

Съвпада с: "изречение"

Коментар: Тук не сме използвали символа ? за мързеливо съвпадение и в резултат символът звезда става "лаком". Може да се заблудим, че съвпадението с него ще е само "из", защото следващата част от шаблона отговаря на подниза "речен", но това не е така. Лакомата звездичка ще приеме за съвпадение всичко до следващото празно място (което вече не се вписва в шаблона \w*), т.е. цялата дума "изречение"
Шаблон: ((.е)+н)?

Съвпада с: ""

Коментар: Тази част от шаблона трябва да търси за повторения на конструкцията символ+"е", последвани от "н", но в момента машината на регулярните изрази се намира на празното място след думата "изречение". Въпреки че вече сме подминали подниз, който отговаря на частта от шаблона, оттук надясно не следва друг такъв подниз. Понеже сме използвали символа за незадължително срещане ?, съвпадението остава единствено празният низ. Ако не бяхме сложили ?, резултатът би бил, че няма никакво съвпадение с целия шаблон, защото машината ще се премести да търси отначало от втория символ в низа, но там вече не е началото и съвпадането с ^ винаги ще пропада.
Шаблон: \s+\w*\s

Съвпада с: " има "

Коментар: Тук например вече не е от значение дали използваме "лако­мо" или "мързеливо" търсене. Има само един единствен вариант, който може да бъде приет за съвпадение и това е точно посоченият. Обърнете отново внимание, че "мързеливото" съвпадане е важно тогава, когато след съответния количествен метасимвол имаме указана конкретна част от шаблона, за която да проверим (както беше преди малко). Тогава "лакомото" търсене поглъща и тази част и нашата проверка не е валидна.
Шаблон: .+?\d{2,5}

Съвпада с: "от две до пет цифри тук: 346"

Коментар: Ето ново потвърждение на горния коментар. Конструкциите .+ и .* винаги поглъщат всичко до края на низа. Когато обаче ги ограничим с ?, ще получим най-малкото възможно съвпадение, така че следващата част от шаблона да продължи да отговаря на низа, както е в случая – искаме от две до пет десетични цифри.
Шаблон: .*?[БИРА]+

Съвпада с: "; и може би РИБА"

Коментар: .*? отговаря на всички символи до главната буква "Р", която е валидно съвпадение с частта [БИРА]+. Тук обаче имаме "лаком" плюс и машината на регулярните изрази продължава да търси напред по низа за срещания на някой от изброените в квадратните скоби символи. Това продължава до запетаята, която вече не е такъв символ. На това място целият шаблон е открил едно пълно съвпадение. Краят на низа не е достигнат, но тъй като не сме сложили знак $ или \Z, това не е пречка. Ако сега се опитаме да търсим повторно (от вече достигнатото място – позицията на запетаята), разбира се няма да получим ново съвпадение, тъй като нашият шаблон започва с ^, а ние сме минали началото.




Сподели с приятели:
1   ...   65   66   67   68   69   70   71   72   73




©obuch.info 2024
отнасят до администрацията

    Начална страница