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), "function">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.