Садржај

Регуларни изрази у програмима

У стандардној библиотеци језика C# постоји солидна подршка за употребу регуларних израза у програмима које пишемо. Да бисмо удобно користили ту подршку, потребно је да на почетку програма најавимо да ћемо користити именски простор у оквиру кога се налазе класе које су нам потребне, тј. да напишемо

using System.Text.RegularExpressions;

Централна класа коју ћемо користити је класа Regex и она представља један регуларан израз. Објекти ове класе не могу да мењају своју дефиницију током рада програма, тј. они су неизмењиви (енгл. immutable). Класа Regex се користи у свим примерима који следе. Многи методи допуштају подешавање осетљивости претраге на велика и мала слова, што ми нисмо користили. Зато је претрага у свим наведеним примерима осетљива на велика и мала слова (то је подразумевано понашање).


Скрећемо вам пажњу на још један детаљ, који може да доведе до збрке. Реч је о томе да карактер \ (бекслеш, енгл. backslash) у програмском језику C# (као и у многим другим) има специјално значење. Тако, да би стринг садржао један бекслеш, у програму је потребно навести два. На пример, путања до фолдера c:\temp се стрингу у програму додељује као

string s = "c:\\temp";

Друга могућност је да се испред ниске дате под наводницима стави знак @. У том случају сви знакови ниске се третирају као обични, тј. губе своје специјално значење (такозвани сирови стринг, енгл. raw string). У том случају, у програму бисмо писали:

string s = @"c:\temp";

Ово су правила писања (тј. синтакса) језика C# и ова правила немају везе са наменом и значењем (семантиком) стрингова који садрже бекслеш карактере. Према томе, иста правила важе и када се стрингом задаје регуларан израз. Тако, регуларан израз \d+, који задаје низ цифара, у програму би могао да се формира на један од ова два начина:

Regex re = new Regex("\\d+");
Regex re = new Regex(@"\d+");

Ми ћемо у програмима користити оба начина записивања стрингова који садрже карактер бекслеш. Приметимо узгред да би формирање регуларног израза \\, који поклапа један знак бекслеш, у програму могло да захтева писање чак 4 бекслеш карактера:

Regex re = new Regex("\\\\");

У том случају, предност бисмо дали сировом стрингу, као лакше читљивом:

Regex re = new Regex(@"\\");

Следи неколико кратких програма, који илуструју различите начине употребе регуларних израза.

Провера постојања уклапања

У овом примеру се за сваки од неколико текстова проверава да ли он садржи поклапање за дати регуларан израз. Коришћени израз представља секвенцу од бар два узастопна слова, од којих је прво велико. За проверу се користи метод IsMatch.

Програм исписује:

Primer provere uklapanja (metod IsMatch)
'Veliko Slovo' -> da
'veLiko Slovo' -> da
'A' -> ne
'mala slova' -> ne

Исти метод може да се употреби за проверу да ли дати текст као целина представља тачно уклапање за дати регуларан израз. Довољно је да се у стринг који описује регуларан израз на почтак и крај додају редом ^ и $.

Налажење првог уклапања

Када нам је осим информације да ли поклапање постоји потребан и текст који представља поклапање за дати израз, можемо да користимо метод Regex.Match. Овај метод враћа објекат који, поред осталог, садржи и прво поклапање датог израза на које се наилази у тексту.

Програм исписује:

Nalazenje prvog poklapanja pomocu Regex.Match
'Veliko Slovo' -> 'Veliko'
'veLiko Slovo' -> 'Liko'
'A' -> nema
'mala slova' -> nema

Налажење свих уклапања

Следећи пример показује како можемо да добијемо колекцију делова текста, који се уклапају у дати регуларан израз. Метод Matches класе Regex као резултат враћа објекат класе MatchCollection, који представља једну такву колекцију. Елементима колекције се приступа помоћу индекса, као код низа. Сваки од елемената има својство Value, помоћу кога добијамо поклопљени део текста који одговара том елементу.

Програм исписује:

Primer upotrebe kolekcije uklapanja (metod Matches)
Tekst: 'Imam Ivanov, Majin i tvoj broj, nemam Ilijin.'
Uklapanja:
poz 0: 'Imam'
poz 5: 'Ivanov'
poz 38: 'Ilijin'

Замене делова текста

У овом примеру видимо како помоћу регуларног израза поједини делови текста могу да се замене неким другим текстом. За то се користи статички метод Replace класе Regex, који подсећа на метод Replace класе string. Наравно, пошто користи регуларне изразе за описивање делова текста које треба заменити, метод Regex.Replace је знатно моћнији од истоименог метода класе string.

У примеру се за претрагу стринга користи регуларан израз \s+, који представља једну или више узастопних белина. Сваки део стринга који се уклапа у овај регуларан израз (тј. свака секвенца узастопних белина), биће замењен једним бланко карактером.

Програм исписује:

Primer zamene u stringu pomocu regexa (metod Regex.Replace)
Tekst pre zamene: 'Primer    viska    belina      u tekstu.'
Tekst posle zamene: 'Primer viska belina u tekstu.'

Израчунавање замене на основу нађеног уклапања

Замена појединих делова текста помоћу регуларних израза је много моћнија него што се то види из претходног примера. Овде поново не можемо да се упуштамо у све могућности, јер их има заиста много, али смо као илустрацију издвојили једну веома практичну и једноставну за употребу. Реч је о томе да не морамо свако нађено уклапање да заменимо истим, фиксираним стрингом, већ замена може да зависи од пронађеног уклапања. Другим речима, замену можемо да израчунавамо на основу сваког конкретног дела текста који представља уклапање за дати регуларан израз.

У следећем примеру користимо један другачији облик метода Replace класе Regex. Овај облик метода Replace као трећи аргумент уместо стринга прима функцију (метод) са тачно одређеним итерфејсом: једини аргумент те функције је типа Match, а функција треба да врати стринг. Стринг који врати ова функција, биће коришћен као замена за нађено уклапање.

У програму испод, првим позивом метода Replace налазимо хексадекадне записе бројева у почетном тексту и замењујемо их декадним, а другим обрнуто, декадне записе замењујемо хексадекадним. Јасно је да у оба случаја стринг који замењује нађено уклапање зависи од самог уклапања, тј. од конкретног броја. Зато у сваком од два позива метода Replace, уместо једне те исте замене у облику стринга, наводимо име одговарајуће функције, која на основу нађеног уклапања израчунава и враћа стринг који се користи као замена. У првом типу замене то је функција HehUDec, а у другом функција DecUHex.

Програм исписује:

s1 = 'ASCII kodovi karaktera 'a' i 'A' su redom 0x41 i 0x61'
s2 = 'ASCII kodovi karaktera 'a' i 'A' su redom 65 i 97'
s3 = 'ASCII kodovi karaktera 'a' i 'A' su redom 0x41 i 0x61'

Пример комбиновања метода Matches и Replace

Методи Regex.Matches и Regex.Replace представљају веома моћно и елегантно средство за обраду текста, поготово када се користе у пару. Зато ћемо употребу ових метода да илуструјемо још једним примером.

Написати програм који у неком кôду, датом у облику стринга, мења начин задавања боје. Прецизније, свако појављивање текста облика rgb(a, b, c), где су a, b, c цели бројеви од 0 до 255, треба заменити текстом облика #AABBCC, где су AA, BB, CC редом исти бројеви, али у двоцифреном хексадекадном запису (са великим словима A-F, ако буду потребна). На пример, за улазни текст

display: flex; color: rgb(34, 12, 64); background-color: rgb(255, 255, 127);

програм треба да испише

display: flex; color: #220C40; background-color: #FFFF7F;

Идеја решења је да делове текста облика rgb(a, b, c) пронађемо и заменимо користећи један позив метода Regex.Replace. Функција која израчунава замену може да користи метод Regex.Matches да из датог уклапања за rgb(a, b, c) издвоји бројеве a, b и c. Након добијања вредности a, b и c, лако се формира запис облика #AABBCC, који треба вратити као резултат функције за израчунавање једне замене за запис боје.

За унето

display: flex; color: rgb(34, 12, 64); background-color: rgb(255, 255, 127);

програм исписује:

display: flex; color: #220C40; background-color: #FFFF7F;

Програм је прилично кратак, с обзиром на релативно сложену замену текста коју обавља. Застаните овде и размислите како би изгледао програм који решава задатак без употребе регуларних израза.

Такође, пробајте да се присетите проблема које сте некада раније решавали без употребе регуларних израза, а у којима би ове могућности значајно поједноставиле решење.

Раздвајање делова текста

Метод Regex.Split, који је илустрован у следећем примеру, сличан је методу Split над стринговима. Као и у случају метода Replace, и овде имамо богатије могућности, јер као раздвајач (сепаратор) можемо да користимо сваки текст који је поклапање за дати регуларан израз. У овом примеру се користи регуларан израз [^\w]+, који описује низ од једног или више знакова, од којих ниједан није алфанумерик.

Програм исписује:

Primer razdvanja teksta pomocu regexa (metod Regex.Split)
Tekst: 'Jedan_dva, tri:4,pet 6.78'
Delovi razdvojeni serijama nealfanumerickih karaktera:
Jedan_dva
tri
4
pet
6
78
(Created using Swinx, RunestoneComponents and PetljaDoc)
© 2022 Petlja
A- A+