Za reference si poglejte RexEgg in phpenthusiast

Funkcije za delo z regularnimi izrazi v PHP

Regularne izraze uporabljamo za iskanje po besedilih. Z regex v nizu znakov iščemo določen vzorec, če je ta prisoten kot rezultat prejmemo TRUE sicer FALSE.

npr: preg_match("/Luka/", $besedilo); #rezultat je TRUE/FALSE

Z regex lahko tudi iščemo dele niza in ga poljubno menjamo (search&replace.)

Zadeva je zelo uporabna pri preverjanju in čiščenju odprtih vnosov uporabnikov spletnih aplikacij.

Kako v PHP torej izgleda regularen izraz? PHP ga prepozna če je zapisan "/takole/"

Sedaj pa k primerom. V spremenljivko z imenom $besedilo bomo vnesli niz znakov.

Iskanje

Najprej si pripravimo besedilo, v katerem bomo iskali vzorce:

<?php
$besedilo = "Moje ime je Toni, Toni Res.";
echo '<b>' . $besedilo . '</b>';
?>
Rezultat izvajanja:
Moje ime je Toni, Toni Res.

preg_match() vrne vrednost 0 ali 1.

<?php
echo preg_match("/Toni/", $besedilo);
?>
Rezultat izvajanja:
1

V praksi funkcijo največkrat uporabimo v kombinaciji z if:

<?php
if (preg_match("/Toni/", $besedilo)){
    echo "Toni was here!";
}
?>
Rezultat izvajanja:
Toni was here!

Če želimo iskanje razbremeniti razlik med velikimi in malimi črkami, regexu dodamo modifikator i, na primer /Toni/i.

<?php
if (preg_match("/toni/i", $besedilo)){
    echo "Toni was here!";
}
?>
Rezultat izvajanja:
Toni was here!

Funkciji lahko dodamo še tretji parameter. Vanj PHP shrani najdeni zadetek.

<?php
if (preg_match("/Toni/", $besedilo, $polje)){
    print_r($polje);
}
?>
Rezultat izvajanja:
Array ( [0] => Toni )

preg_match() vrne samo prvi zadetek, če je iskani niz prisoten. Če želimo poiskati vse zadetke, uporabimo funkcijo preg_match_all().

<?php
if (preg_match_all("/Toni/", $besedilo, $polje)){
    print_r($polje);
}
?>
Rezultat izvajanja:
Array ( [0] => Array ( [0] => Toni [1] => Toni ) )

Z oklepaji lahko označimo skupine. Tako lahko posebej zajamemo del zadetka.

<?php
if (preg_match_all("/To(ni)/", $besedilo, $polje)){
    print_r($polje);
}
?>
Rezultat izvajanja:
Array ( [0] => Array ( [0] => Toni [1] => Toni ) [1] => Array ( [0] => ni [1] => ni ) )

Izpišimo še vsebino polja, ki ga dobimo kot rezultat iskanja:

<?php
if (preg_match_all("/To(ni)/", $besedilo, $polje)){
    echo $polje[0][0] . "<br>";
    echo $polje[1][1] . "<br>";
}
?>
Rezultat izvajanja:
Toni
ni

Nadomeščanje

Za zamenjavo besedila uporabimo preg_replace().

<?php
$besedilo2 = preg_replace("/Toni/", "Luka", $besedilo);
echo $besedilo2;
?>
Rezultat izvajanja:
Moje ime je Luka, Luka Res.

Razbijanje besedila (string split)

Za razbijanje besedila na več delov z uporabo izbranega niza znakov uporabimo funkcijo preg_split($regex, $string).

<?php
$besedilo3 = "html, css, javascript, php";
echo $besedilo3; 

echo "<br>"; 

$jeziki = preg_split("/,/", $besedilo3);
print_r($jeziki);

?> 
Rezultat izvajanja:
html, css, javascript, php
Array ( [0] => html [1] => css [2] => javascript [3] => php )

Iskanje znotraj polja

Do zdaj smo regularne izraze uporabljali na enem nizu znakov. V PHP-ju pa lahko regex uporabimo tudi za iskanje znotraj polja.

Za to uporabimo funkcijo preg_grep(). Ta pregleda vse elemente polja in vrne samo tiste, ki ustrezajo podanemu regularnemu izrazu.

<?php
print_r($jeziki);
echo "<br>";

$output = preg_grep('/h[tp]/', $jeziki);
print_r($output);
?>
Rezultat izvajanja:
Array ( [0] => html [1] => css [2] => javascript [3] => php )
Array ( [0] => html [3] => php )

Vzorci za iskanje z regularnimi izrazi

Regularni izrazi uporabljajo posebne znake (metaznake), s katerimi opišemo vzorce za iskanje.

Posebni (rezervirani) znaki

<?php
$besedilo = "Moje ime je Toni, Toni Res.";

echo preg_match("/Toni/", $besedilo);
echo preg_match("/./", $besedilo);
echo preg_match("/(o|e)/", $besedilo);
echo preg_match("/(a|N)/", $besedilo);
?>
Rezultat izvajanja:
1
1
1
0

Začetek in konec niza

<?php
$besedilo2 = "Moje ime je Toni, Toni";

preg_match_all("/Toni/", $besedilo2, $polje);
print_r($polje);

preg_match_all("/Toni$/", $besedilo2, $polje);
print_r($polje);

preg_match_all("/^.*$/", $besedilo2, $polje);
print_r($polje);
?>
Rezultat izvajanja:
Array ( [0] => Array ( [0] => Toni [1] => Toni ) )
Array ( [0] => Array ( [0] => Toni ) )
Array ( [0] => Array ( [0] => Moje ime je Toni, Toni ) )

Iskanje posebnih znakov

Nekateri znaki imajo v regexu poseben pomen, zato jih moramo zapisati z escape znakom \, če jih želimo iskati dobesedno:

V nizu znakov Moje $ime ^ je Toni, Toni, bi radi poiskali $ime

<?php
$besedilo3 = "Moje \$ime ^ je Toni, Toni";

preg_match_all("/\\$.../", $besedilo3, $polje);
print_r($polje);
?>
Rezultat izvajanja:
Array ( [0] => Array ( [0] => $ime ) )

Vrste znakov (character classes)

Z oglatimi oklepaji določimo množico znakov:

<?php
echo preg_match("/[abc]/", $besedilo);
echo preg_match("/[ijk]/", $besedilo);
echo preg_match("/[^Na]/", $besedilo);
echo preg_match("/[a-c]/", $besedilo);
echo preg_match("/[a-zA-Z]/", $besedilo);
echo preg_match("/[0-9]/", $besedilo);
?>
Rezultat izvajanja:
0
1
1
0
1
0

Bližnjice za vrste znakov

<?php
preg_match_all("/\\$\\w\\D\\S/", $besedilo3, $polje);
print_r($polje);
?>
Rezultat izvajanja:
Array ( [0] => Array ( [0] => $ime ) )

Modifikator i

<?php
echo preg_match("/MOj/i", $besedilo);
?>
Rezultat izvajanja:
1

Števniki

Števniki določajo, kolikokrat se mora določen znak ali vzorec pojaviti.

Najprej preverimo, ali se v besedilu sploh pojavi velika črka T:

<?php
echo preg_match("/T/", $besedilo);
?>
Rezultat izvajanja:
1

V zavitih oklepajih lahko določimo, kolikokrat se mora znak ponoviti:

<?php
echo preg_match("/T{1}/", $besedilo) . "<br>";
echo preg_match("/T{2}/", $besedilo) . "<br>";
?>
Rezultat izvajanja:
1
0

Določimo lahko tudi spodnjo in zgornjo mejo:

<?php
echo preg_match("/T{0,1}/", $besedilo) . "<br>";
echo preg_match("/T{2,3}/", $besedilo) . "<br>";
echo preg_match("/T{1,}/", $besedilo) . "<br>";
?>
Rezultat izvajanja:
1
0
1

Znak * pomeni: ničkrat ali večkrat.

<?php
echo preg_match("/T*/", $besedilo) . "<br>";

preg_match_all("/T*/", $besedilo, $polje);
print_r($polje);
?>
Rezultat izvajanja:
1
Array ( [0] => Array ( [0] => [1] => [2] => [3] => [4] => [5] => [6] => [7] => [8] => [9] => [10] => [11] => [12] => T [13] => [14] => [15] => [16] => [17] => [18] => T [19] => [20] => [21] => [22] => [23] => [24] => [25] => [26] => [27] => ) )

Če želimo shraniti tudi znake, ki sledijo črki T, lahko uporabimo piko in števnik:

<?php
preg_match_all("/T.*/", $besedilo, $polje);
print_r($polje);
?>
Rezultat izvajanja:
Array ( [0] => Array ( [0] => Toni, Toni Res. ) )

Iskanje lahko tudi omejimo, na primer do črke e:

<?php
preg_match_all("/T.*e/", $besedilo, $polje);
print_r($polje);
?>
Rezultat izvajanja:
Array ( [0] => Array ( [0] => Toni, Toni Re ) )

Znak + pomeni: enkrat ali večkrat.

<?php
preg_match_all("/T+/", $besedilo, $polje);
print_r($polje);
?>
Rezultat izvajanja:
Array ( [0] => Array ( [0] => T [1] => T ) )

Poglejmo še primer z drugim nizom:

<?php
$besedilo4 = "bat a 1000";

preg_match_all("/\s\w\s\d\d\d\d/", $besedilo4, $polje);
echo $polje[0][0];

echo "<hr>";

preg_match_all("/\D{3}/", $besedilo4, $polje);
print_r($polje);
?>
Rezultat izvajanja:
a 1000
Array ( [0] => Array ( [0] => bat [1] => a ) )

Leni in požrešni izrazi - Lazy and greedy expressions

Nekateri števniki v regularnih izrazih so požrešni (greedy), kar pomeni, da poskušajo zajeti čim več znakov. Če jim dodamo vprašaj ?, postanejo leni (lazy) in zajamejo čim manj znakov.

Za primer malce predelajmo naše besedilo:

<?php
$besedilo5 = "Moje 1ime2 je Toni, 1Toni2 Res.";
echo $besedilo5 . "<br>";
?>
Rezultat izvajanja:
Moje 1ime2 je Toni, 1Toni2 Res.

Najprej primer za *, ki je požrešen (greedy) števnik:

<?php
preg_match_all("/1.*2/", $besedilo5, $polje);
print_r($polje);
?>
Rezultat izvajanja:
Array ( [0] => Array ( [0] => 1ime2 je Toni, 1Toni2 ) )

Izraz .* pomeni: zajemi karkoli in to naredi čim bolj na široko. Zato regex ujame vse od prve 1 do zadnje 2.

Če dodamo vprašaj in uporabimo *?, dobimo leni (lazy) števnik:

<?php
preg_match_all("/1.*?2/", $besedilo5, $polje);
print_r($polje);
?>
Rezultat izvajanja:
Array ( [0] => Array ( [0] => 1ime2 [1] => 1Toni2 ) )

Izraz .*? pomeni: zajemi čim manj znakov, samo toliko, da se vzorec še ujema. Zato tukaj dobimo dva ločena zadetka.

Tak pristop je zelo uporaben, kadar iščemo vsebino znotraj določenih oznak.

<?php
$besedilo6 = "Trraraaa! <titl>Regularni izrazi v PHP</titl> Jeah.";
echo $besedilo6 . "<hr>";

preg_match_all("/<titl>.*?<\/titl>/", $besedilo6, $polje);
print_r($polje);
?>
Rezultat izvajanja:
Trraraaa! <titl>Regularni izrazi v PHP</titl> Jeah.
Array ( [0] => Array ( [0] => Regularni izrazi v PHP ) )

Skupine in reference (backreference)

Z oklepaji () lahko v regularnem izrazu definiramo skupine. Na te skupine se lahko kasneje sklicujemo pri zamenjavi besedila.

Sklicujemo se z znakom $:

Tipičen primer uporabe je sprememba formata datuma iz YYYY-MM-DD v DD. MM. YYYY.

<?php
$datum = "2021-03-12";
echo $datum . "<br>";

echo preg_replace("/(2021)-(03)-(12)/", "$3. $2. $1", $datum);
?>
Rezultat izvajanja:
2021-03-12
12. 03. 2021

Zgornji primer deluje, vendar je zelo specifičen (ujemajo se samo točno določene vrednosti).

Bolj splošen zapis uporabimo z razredi znakov in števniki:

<?php
echo preg_replace("/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})/", "$3. $2. $1", $datum);
?>
Rezultat izvajanja:
12. 03. 2021