Wyrażenia regularne

PHP obsÅ‚uguje 2 rodzaje wyrażeÅ„ regularnych: zgodne ze standardem POSIX (które tu skrótowo opiszÄ™) i te zastosowane w jÄ™zyku Perl (opis tych wyrażeÅ„ zostanie tu umieszczony w terminie późniejszym). Wyrażenia regularne sÄ… to ciÄ…gi znaków, które można dopasować do różnych innych ciÄ…gów. Można to porównać do tzw. wildcardów używanych przy operacjach na plikach, gdzie gwiazdka (’*’) zastÄ™powaÅ‚a dowolnÄ… ilość dowolnych znaków, czyli wyrażenie '*.*’ (w przypadku systemów dosowych) bÄ™dzie pasowaÅ‚o do plików o dowolnej nazwie i dowolnym rozszerzeniu.

Wyrażenia regularne sÄ… o wiele bardziej skomplikowane i też dziaÅ‚ajÄ… trochÄ™ inaczej. W wildcardach gwiazdka zastÄ™powaÅ‚a dowolnÄ… ilość dowolnych znaków, natomiast w wyrażeniach regularnych dziaÅ‚a niejako 'wstecz’ – opisuje dowolnÄ… iloÅ›c powtórzeÅ„ (zero lub wiÄ™cej) poprzedniego znaku, czyli wyrażenie 'a*’ bÄ™dzie pasowaÅ‚o do 'aaa’, 'aaaaa’ ale i 'b’, ponieważ w tym wyrażeniu nie ma ograniczenia że nie może być nic oprócz 'a’. Takie ograniczenie można uzyskać dziÄ™ki znakom '^’ i '$’. Znak '^’ rozumiany jest jako poczÄ…tek linii, a '$’ jako koniec. Tak wiÄ™c jeÅ›li chcemy znaleźć ciÄ…g, który bÄ™dzie pusty lub bÄ™dÄ… w nim same litery 'a’, to powinien on wyglÄ…dać tak: '^a*$’. Natomiast jeÅ›li 'a’ musi wystÄ…pić przynajmniej raz, to zamiast znaku '*’ należy użyć znaku '+’ – oznacza on jedno lub wiÄ™cej powtórzeÅ„ ostatniego znaku. Istnieje też możliwość użycia znaku ’?’, który oznacza żadne lub jedno wystÄ…pienie poprzedzajÄ…cego ten znak atomu, znaku lub zakresu. Można także podać konkretnÄ… ilość wystÄ…ieÅ„ znaku, atomu lub przedziaÅ‚u przez użycie za raz po nich nawiasów klamrowych. JeÅ›li bÄ™dzie w nich jedna liczba, to bÄ™dzie ona okreÅ›laÅ‚a dokÅ‚adnie danÄ… liczbÄ™ powtórzeÅ„. JeÅ›li bÄ™dzie w nich liczba a po niej przecinek, to wyrażenie bÄ™dzie musiaÅ‚o wystÄ…pić przynajmniej tyle razy ile wynosi podana liczba. Istnieje także możliwość podania w nawiasach klamrowych dwóch liczb oddzielonych przecinkiem, co oznacza powtórzenie wyrażenia minimalnie tyle razy ile pierwsza liczba, a maksymalnie tyle ile druga (wÅ‚Ä…cznie).

Zamiast pojedyÅ„czego znaku można podać zakres wyrazów lub caÅ‚e wyrażenie – tzw. atom. Zakres znaków podaje siÄ™ w nawiasach kwadratowych. W tych nawiasach można podać pojedyÅ„cze znaki jeden po drugim (np. '[egt]’), przedziaÅ‚ liter (np. wszystkie maÅ‚e litery: '[a-z]’ – uwaga, zakres ten nie obejmuje polskich znaków diakratycznych), lub poÅ‚Ä…czenie tych dwóch typów (np. '[a-zA-ZąćęłńóśźżĄĆĘŁŃÓŚŹŻ]’ okreÅ›la wszystkie litery). W nawiasach kwadratowych można podawać także symbole. JeÅ›li w przedziale potrzebny jest znak myÅ›lnika, który normalnie używany jest do okreÅ›lania przedziałów znaków, to trzeba umieÅ›cić go na pierwszej lub ostatniej pozycji w nawiasach kwadratowych. W nawiasach kwadratowych znak '^’ ma inne znaczenie. Oznacza ono zaprzeczenie, czyli jeÅ›li umieÅ›ci siÄ™ je przed znakiem lub zakresem, to ten znak lub zakres nie bÄ™dzie mógÅ‚ siÄ™ pojawić w tym miejscu.

JeÅ›li znak może być dowolny, to zamiast konkretnej litery, znaku czy przedziaÅ‚u można użyć symbolu kropki (’.’). PrzykÅ‚adowo wyrażenie '^.*$’ opisuje dowolny ciÄ…g skÅ‚adajÄ…cy siÄ™ z dowolnej iloÅ›ci dowolnych znaków.

Atom, czyli wyrażenie zawarte w nawiasach okrÄ…gÅ‚ych, może zawierać (prawie) dowolnÄ… ilość znaków, zakresów i innych atomów. Pozwala to na dopasowywanie powtarzajÄ…cych siÄ™ fragmentów ciÄ…gów znaków. Dobrym przykÅ‚adem jest sprawdzenie, czy dany ciÄ…g znaków jest Å›cieżkÄ… w systemie uniksowy. PrzykÅ‚adowy ciÄ…g to '/usr/local/bin/php’. Wyrażenie regularne powinno wyglÄ…dać tak: '(/([a-Z])+)*(/([a-Z\.]))’. Pierwszy atom okreÅ›la dowolnÄ… ilość katalogów (na poczÄ…tku znak slash a po nim dowolna ilość liter o dowolnej wielkoÅ›ci) po czym nazwa pliku – po atomie nie ma żadnego znaku okreÅ›lajÄ…cego ilość wystÄ…pieÅ„, a wiÄ™c musi wystÄ…pić dokÅ‚adnie raz.

PomiÄ™dzy atomami, znakami lub zakresami można postawić znak '|’, który oznacza logicznÄ… operacjÄ™ 'OR’, czyli poprostu oznacza że może wystÄ…pić jeden lub drugi atom (ten przed znakiem '|’ i ten po).

JeÅ›li w wyrażeniu regularnym musisz podać znak, który byÅ‚by interpretowany inaczej niż zamierzamy (np. jeÅ›li musisz znaleźć ciÄ…g zaczynajÄ…cy siÄ™ od gwiazdki po której jest dowolna ilość liter A, to nie można 'normalnie’ podać znaku gwiazdki, ponieważ byÅ‚by on interpretowany jako dowolna ilość powtórzeÅ„ ostatniego znaku), to należy zamienić ten znak na tzw. 'escape sequence’ – poprostu należy przed tym znakiem wstawić znak ukoÅ›nika (’\’). Takie znaki to: '(’, ’)’, '[’, ’]’, '{’, ’}’, '\’, '*’, '|’, '^’, '$’, ’?’. JeÅ›li w wyrażeniu trzeba użyć wÅ‚aÅ›nie znaku ukoÅ›nika, to należy wpisać dwa ukoÅ›niki, jeden po drugim.

Porównywanie ciągów

Podstawową funkcją służącą do porównywania ciągów jest strcmp(). Jako jedyne 2 parametry przyjmuje ona ciągi tekstowe, które mają być porównane. Funkcja ta zwraca wartość 0 jeśli ciągi są takie same, wartość większą od zera jeśli pierwszy ciąg jest większy od drugiego lub wartość mniejszą od zera jeśli pierwszy ciąg jest mniejszy od drugiega. Funkcja ta rozróżnia wielkość znaków. Jeśli zachodzi potrzeba porównania dwóch ciągów, to należy użyć funkcji strcasecmp() (parametry i wartości zwracane takie jak przy funkcji strcmp). Istnieje także funkcja strncmp(), która porównuje tylko taką ilość początkowych znaków z podanych ciągów, jaka została podana jako trzeci parametr tej funkcji. Oczywiście istnieje też wersja funkcji strncmp() ignorująca wielkość znaków: strncasecmp().

Istnieje racjonalne wytÅ‚umaczenie dlaczego ciÄ…gi należy porównywać za pomocÄ… funkcji strcmp() lub pochodnych a nie zwykÅ‚ego operatora porównania '==’. Interpreter jÄ™zyka PHP stara siÄ™ być mÄ…drzejszy od programisty automatycznie konwertujÄ…c typy zmiennych aby przy porównywaniu ich byÅ‚y takie same.

Przykład 9.1. Proste porównywanie ciągów


<?php
$a=0;
$b="0papa";
if($a==$b)
   echo "Tak";
else
   echo "Nie";

?>

            

Powyższy przykÅ‚ad wyÅ›wietli „Tak”, pomimo że te zmienne sÄ… różne. Dlaczego? Ponieważ PHP automatycznie konwertuje zmiennÄ… $b do typu liczbowego, przez co zamieniana jest ona na liczbowÄ… wartość 0 (która faktycznie jest na pierwszym miejscu ciÄ…gu – pozostaÅ‚e znaki sÄ… odrzucane ponieważ nie sÄ… liczbami). Można teoretycznie użyć operatora „===” – porównania nie tylko wartoÅ›ci, ale też typu.

Przykład 9.2. Operator identyczności


<?php
$a=0;
$b="0";
if($a===$b)
   echo "Tak";
else
   echo "Nie";

?>

            

Powyższy przykÅ‚ad wyÅ›wietli „Nie”, bo pomimo że obie zmienne majÄ… wartość 0, to zmienna $a jest liczbÄ…, a zmienna $b ciÄ…giem tekstowym.

Funkcja strstr() sÅ‚uży do sprawdzania, czy podany ciÄ…g jest fragmentem innego ciÄ…gu. Funkcja przyjmuje dwa parametry: przeszukiwany ciÄ…g (’stóg siana’) i szukany ciÄ…g (’igÅ‚a’). JeÅ›li dany ciÄ…g jest fragmentem podanego, to zwracany jest ciÄ…g – fragment przeszukiwanego ciÄ…gu od pierwszego wystÄ…pienia szukanego ciÄ…gu do koÅ„ca.

Przykład 9.3. Zastosowanie funkcji strstr


<?php

$email = "prezydent@polska.pl";
$domena = strstr($email, "@");
echo $domena;

?>

            

Powyższy przykÅ‚ad wyÅ›wietli „@polska.pl”. JeÅ›li ciÄ…g nie jest znaleziony, to zwracana jest wartość FALSE, dziÄ™ki czemu funkcjÄ™ tÄ… można używać w instrukcjach warunkowych.

Przykład 9.4. Użycie funkcji strstr() w warunku


<?php

$email = "prezydent@polska.pl";
if(strstr($email, "polska")!==False)
   echo "Email ma w sobie słowo 'polska'";

?>

            

Przy używaniu tej funkcji trzeba pamiÄ™tać o jednej rzeczy. Łatwo jest pomylić zwracanÄ… wartość 'znaleziono na pozycji 0′ i 'nie znaleziono’, ponieważ wartość logiczna 'true’ przy porównaniach może być skonwertowana do wartoÅ›ci liczbowej 0. Aby tego uniknąć należy używać operatora porównania ze sprawdzaniem typu (’===’ lub ’!==’).

Istnieje także wersja tej funkcji ignorująca wielkość znaków: stristr()(przyjmowane parametry i zwracane wartości takie jak przy funcji strstr).

Pomimo, że funkcji strstr() można używać do sprawdzania czy jeden ciąg jest częścią drugiego, to jest ona nieefektywna. Lepiej skorzystać z funkcji strpos().

Do sprawdzania, czy ciąg tekstowy pasuje do wyrażenia regularnego używa się funkcji ereg() która jako pierwszy parametr przyjmuje wyrażenie regularne, a jako drugi ciąg który ma być porównany z wyrażeniem. Istnieje także wersja tej funkcji ignorująca wielkość znaków: eregi(). Zwracana jest wartość true jeśli ciąg pasuje do wyrażenia regularnego, a false jeśli nie pasuje.

Zazwyczaj przy instrukcjach warunkowych w którch korzysta się z funkcji sprawdzającej jakiś warunek, funkcja ta zwraca wartość TRUE jeśli warunek jest spełniony, np. funkcja ereg zwraca tą wartość jeśli dany ciąg pasuje do danego wyrażenia regularnego.

Przykład 9.5. Użycie funkcji ereg w warunku


<?php

if(ereg(".*", $ciag))
  echo "Znalazłem!";

?>

            

W przypadku funkcji strcmp() i pochodnych jest trochę inaczej, gdyż jeśli ciągi są identyczne to zwracana jest wartość 0 uznawana za logiczne FALSE.

Przykład 9.6. Porównywanie ciągów przy użyciu funkcji strcmp


<?php

if(!strcmp("cośtam", $string))
   echo "$string to cośtam!";

?>

            

Wyciąganie fragmentów ciągów

Bardzo czÄ™sto zachodzi potrzeba wyciÄ…gniÄ™cia ze zmiennej tekstowej tylko pewnego jej fragmentu. NajÅ‚atwiej jest jeÅ›li znana jest dÅ‚ugość wyciÄ…ganego fragmentu i pozycja w której siÄ™ znajduje w zmiennej. Np. jeÅ›li ze zmiennej zawieracjÄ…cej „1992/11/19” chcemy wyciÄ…gnąć rok. Wiadomo, że rok jest na samym poczÄ…tku i ma 4 znaki. NajÅ‚atwiej jest użyć funkcji substr(). Jako pierwszy paramter pobiera ona ciÄ…g z którego bÄ™dzie wycinany fragment, jako drugi miejsce, z którego bÄ™dzie rozpoczÄ™te wycinanie (0 jeÅ›li od pierwszego znaku, liczba ujemna jeÅ›li ma to być ilość znaków od koÅ„ca), a ostatni, opcjonalny parametr wskazuje ilość znaków do wyciÄ™cia (jeÅ›li pominie siÄ™ ten parametr, to zwrócony zostanie pod-ciÄ…g od wskazanego znaku poczÄ…tkowego do koÅ„ca ciÄ…gu).

Przykład 9.7. Użycie funkcji substr()


<?php

$data = "1992/11/19";

$rok = substr($data, 0, 4);

?>

            

Ale to oczywiście najprostsza z sytuacji. Bardziej skomplikowanym przykładem będzie wyciągnięcie z tej samej daty wszystkich pól. Można oczywiście 3 razy używać funkcję substr, ale wydajniejszą metodą jest rozbicie tekstu na tablicę korzystając z opisanej przy okazji tablic funkcji explode.

Przykład 9.8. Zastosowanie funkcji explode


<?php

$data = "1992/11/19";

$tablica = explode("/", $data);

?>

            

W powyższym przykÅ‚adzie, w pierwszym elemencie tablicy znajdować siÄ™ bÄ™dzie rok, w drugim a w trzecim dzieÅ„ z podanej daty. Można także użyć tej funkcji w bardziej skomplikowany sposób – do rozbicia ciÄ…gu zawierajÄ…cego datÄ™ i czas.

Przykład 9.9. Rozszerzony przykład użycia funkcji explode


<?php

$tekst = "19/11/1982 01:43:12";

$dataiczas = explode(" ", $tekst);
$data = explode("/", $dataiczas[0]);
$czas = explode(":", $dataiczas[1]);

?>

            

Teraz tablica $data zawiera dzień, miesiąc i rok a tablica $czas godzinę, minutę i sekundę. Ale co jeśli godzina zawiera setne części sekundy, które podawane są po kropce? Robić jeszcze jedno rozbicie? To już za dużo. Lepiej jest użyć funkcji split(), która funcjonuje w podobny sposób co explode, ale rozbija tekst nie za pomocą zwykłych ciągów znaków, ale za pomocą wyrażeń regularnych. Tak więc cały string zawierający datę i czas (z setnymi sekundami włącznie) można rozbić za pomocą jednego wywołania funkcji split().

Przykład 9.10. Zastosowanie funkcji split()


            

Teraz w tablicy $tablica znajować się będą po kolei: dzień, miesiąc, rok, godzina, minuta, sekunda i setna część sekundy.

Może też zajść potrzeba wyciągnięcia fragmentu ciągu do jakiegoś znaku, np. pierwszego zdania z jakiegoś tekstu. Wtedy można użyć połączenia funkcji substr() i strpos(), która zwraca numer znaku gdzie znajduje się pierwsze wystąpienie ciągu podanego jako drugi parametr w ciągu podanym jako pierwszy parametr, lub false jeśli pierwszy ciąg nie zawiera w sobie drugiego. A więc pierwsze zdanie podanego ciągu można uzyskać tak:

Przykład 9.11. Zastosowanie funkcji strpos()


<?php

$tekst = "To jest tekst. Tego zdania nie będzie widać.";

$zdanie = substr($tekst, 0, strpos($tekst, "."));

?>

            

Zmienna $zdanie powinna zawierać „To jest tekst” – bez kropki na koÅ„cu.

Podmienianie fragmentów ciągów

Do podmiany całych fragmentów ciągu służy funkcja str_replace(), która przyjmuje 3 parametry: ciąg który ma być podmieniony, ciąg na który ma być podmieniony i ciąg którego fragmenty będą podmieniane. A więc wszystkie wystąpienia pierwszego ciągu w trzecim ciągu są zamieniane na drugi ciąg.

Notatka

Zmienna podawana jako trzeci parametr nie jest zmieniana. Poprawiony ciÄ…g jest zwracany przez funkcjÄ™.

Przykład 9.12. Podmiana tekstu przy pomocy funkcji str_replace()


<?php

$tekst = "Jeśli jesteś za podnieś rękę";

$wynik = str_replace("za", "przeciw", $tekst);

?>

            

Zmienna $wynik bÄ™dzie zawieraÅ‚a tekst „JeÅ›li jesteÅ› przeciw podnieÅ› rÄ™kÄ™”.

Od wersji PHP 4.0.5 każdy z parametrów funkcji str_replace może być tablicą. A więc jeśli trzeci parametr jest tablicą, to operacja podmany tekstów jest wykonywana jest na każdym elemencie tablicy, a zwracana zmienna także jest tablicą. Natomiast jeśli tablicami są pierwszy i drugi parametr, to każdy element z pierwszej tablicy jest podmieniany na odpowiadający mu element drugiej tablicy. Jeśli pierwsza tablica ma więcej elementów niż druga, to te elementy z pierwszej tablicy które nie mają odpowiednika w drugiej zamieniane są na puste ciągi. Pozwala to na wiele podmian za jednym wywołaniem funkcji str_replace.

Podmienianie znaków w ciągach

W PHP można także znaleźć funkcję, która zamieni wszystkie wystąpienia podanych znaków na inne znaki. Dzięki temu można np. usunąć wszystkie polskie znaki diakratyczne albo skonwertować te znaki na inną stronę kodową. Funkcja ta nazywa się strtr i przyjmuje 3 parametry: ciąg w którym będą podmieniane znaki, ciąg zawierający znaki do podmiany i ciąg zawierający znaki, na które te podane w drugim parametrze będą podmienione. W poniższym przykładzie polskie znaki diakratyczne zostaną zamieniane na odpowiadające im znaki zestawu ASCII.

Przykład 9.13. Usuwanie polskich znaków diakrytycznych


<?php

$tekst = 'Zażółć gęślą jaźń';

$wynik = strtr($tekst, 'ĘÓĄŚŁŻŹŃęóąśłżźćń', 'EOASLZZCNeoaslzzcn');
   
?>

            

Przy pomocy tej funkcji możliwe jest też konwertowanie ciÄ…gów tekstowych pomiÄ™dzy stronami kodowymi. Poniżej zdefiniowane zostaÅ‚y dwie funkcje – pierwsza konwertuje polskie znaki diakrytyczne ze standardu Win-1250 na ISO-1250-2, natomiast druga – odwrotnie.

Przykład 9.14. Translacja polskich znaków diakrytycznych


<?php

function win2iso($str)
{
   return strtr($str, "\xA5\x8C\x8F\xB9\x9C\x9F",
      "\xA1\xA6\xAC\xB1\xB6\xBC");
}

function iso2win($str)
{
   return strtr($str, "\xA1\xA6\xAC\xB1\xB6\xBC",
      "\xA5\x8C\x8F\xB9\x9C\x9F");
}

?>

            

Inne funkcje

htmlspecialchars($string)

funkcja ta zamienia znaki używane w kodzie HTML na takie, które zostaną wyświetlone prawidłowo, a nie będą interpretowane przez przeglądarkę jako znacznik HTML. Wszystkie wystąpienia znaku < zostają zamienione na znak &lt; a znaku > na &gt;. Funkcja ta jest bardzo przydatna gdy zachodzi potrzeba wyświetlenia kodu źródłowego HTML, który nie powinien być interpretowany przez przeglądarkę.

htmlentities($string)

jest to rozszerzona wersja funkcji htmlspecialchars(). Zamienia wszystkie znaki na odpowiadające im kody HTML, jeśli takowe istnieją. Np. wszystkie wystąpienia znaku ó zamieniane są na &oacute.

chop($string), trim($string), ltrim($string), rtrim($string)

funkcje te majÄ… za zadanie usuniÄ™cie 'biaÅ‚ych znaków’ (spacja, tabulator, znacznik nowego wiersza) z poczÄ…tku lub koÅ„ca ciÄ…gu. Funkcja chop(), tak jak i rtrim(), usuwa te znaki z koÅ„ca ciÄ…gu, ltrim() z poczÄ…tku a trim() i z poczÄ…tku i koÅ„ca.

nl2br($string)

bardzo przydatna funkcja sÅ‚użąca do zamiany znaków koÅ„ca wiersza na HTML’owe znaczniki <BR> (bardzo przydatne przy wyÅ›wietlaniu sformatowanego tekstu w oknie przeglÄ…darki).

strip_tags($string)

usuwa wszystkie znaczniki HTML z podanego jako parametr ciÄ…gu. Możliwe jest podanie opcjonalnego drugiego parametru – ciÄ…gu, który zawiera nazwy znaczników HTML, które majÄ… zostać pozostawione.