Przetwarzanie tekstu - Podstawy PHP

Transkrypt

Przetwarzanie tekstu - Podstawy PHP
Artykuł pobrano ze strony eioba.pl
Przetwarzanie tekstu - Podstawy PHP
Przetwarzanie tekstu
Największa ilość wykonywanych w PHP operacji dotyczy danych tekstowych oraz ich obróbki. Poznamy tutaj
kilkanaście funkcji, które będą nam pomocne. W następnym rozdziale zaznajomimy się natomiast z podstawami
wyrażeń regularnych pozwalających na znacznie bardziej zaawansowane manipulacje oraz dokładniejszą kontrolę
poprawności.
Wyszukiwanie ciągów
Podstawową funkcją wyszukiwania jednego ciągu w drugim jest strpos() posiadająca także wariant stripos(), w
którym nie gra roli wielkość liter. Użyliśmy jej już raz podczas pisania księgi gości do sprawdzenia, czy
wprowadzony adres WWW zawiera identyfikator protokołu. Przypomnijmy jeszcze raz ten skrypt:
<?php
$adresy = array( =>
'http://www.wikibooks.pl',
'www.wikibooks.pl'
);
foreach($adresy as $adres)
{
if(strpos($adres, 'http://') === )
{
// jest http://
echo '<p>'.$adres.'</p>';
}
else
{
// nie ma http://
echo '<p>http://'.$adres.'</p>';
}
}
?>
Funkcje działają następująco: za pierwszy parametr podajemy ciąg do przeszukania, za drugi - szukany, a
następnie patrzymy na rezultat. strpos() powinien zwrócić nam pozycję, od której zaczyna się tekst, którego
szukamy, przy czym znaki liczone są od zera. Jeżeli nic nie zostanie znalezione, dostaniemy wartość false. Z tego
powodu powinniśmy zawsze wykorzystywać te funkcje z operatorami === oraz !== sprawdzającymi nie tylko
wartość, ale i typ zwracanych danych. Oto interesująca sztuczka, która w jednej linijce pozwoli nam na
sprawdzenie, czy ciąg został znaleziony i EWENTUALNE pobranie pozycji jego wystąpienia:
<?php
$zrodlo = 'Litwo, ojczyzno moja!';
$szukany = 'moja';
if(($id = strpos($zrodlo, $szukany)) !== false) // wlasnie tu
{
echo 'Szukany ciąg zaczyna się w znaku '.$id;
}
else
{
echo 'Nie znaleziono szukanego ciągu';
}
?>
Jeśli nie rozumiesz zastosowanej tu sztuczki, przypomnij sobie zabawy z wyrażeniami na początku podręcznika.
PHP najpierw wykona przypisanie do zmiennej $id: $id = strpos($zrodlo, $szukany). Operator zwróci przypisywaną
wartość, która zostanie wykorzystana z kolei do sprawdzenia, czy coś zostało znalezione: [wyrażenie] !== false. W
ten sposób mamy jednocześnie wykonane i sprawdzenie, i pozycję pierwszego wystąpienia w zmiennej $id.
Powyższe dwie funkcje znajdują pierwsze wystąpienie szukanego ciągu. Jeżeli chcemy znaleźć ostatnie, musimy
posłużyć się funkcjami strrpos() i strripos().
Modyfikacja ciągów
Zajmiemy się teraz dosyć rozległą kwestią modyfikacji oraz obróbki tekstu. Jest ona niezwykle ważna, jeżeli
zamierzasz pisać aplikacje typu forum dyskusyjnego czy księgi gości. Z wiadomych przyczyn nigdy nie powinniśmy
zezwolić internaucie na wypisywanie wszystkiego, co mu się podoba i musimy poddać jego tekst elektronicznej
obróbce. Za jej pomocą można również ułatwiać formatowanie tekstu. Przykładowo, treść źródłowa tego
podręcznika zapisana jest w specjalnym zestawie znaczników dostosowanym do specyfiki Wikibooks. Odpowiednia
biblioteka zajmuje się jego przetwarzaniem na język HTML. Tak złożonymi kwestiami nie będziemy się tu jednak
zajmować. Wspomniane zostaną przede wszystkim te funkcje dostępne w PHP. Bardziej zaawansowane algorytmy
musisz już zaprogramować samodzielnie.
Nagłówki na stronach internetowych przybierają różne style, w zależności od pełnionych funkcji. Część z nich
pisana jest kapitalikami, w innych z dużych liter zaczyna się każdy wyraz. Wcale nie musimy zmuszać redaktorów,
by to oni zajmowali się wprowadzaniem tytułów w wymaganej postaci. Istnieją odpowiednie algorytmy, które
wykonają to za nich. Choć obecnie zadanie sformatowania tytułów można z powodzeniem zrealizować już z
wykorzystaniem CSS-a, PHP posiada szereg funkcji do konwersji dużych liter na małe. Pamiętajmy wszak, że PHP to
nie tylko strony internetowe.
<?php
$tekst = 'php jest językiem programowania skryptowego zaprojektowanego dla stron internetowych';
echo '<p>Kapitaliki: '.strtoupper($tekst).'</p>';
echo '<p>Z dużej litery: '.ucfirst($tekst).'</p>';
echo '<p>Każdy wyraz z dużych liter: '.ucwords($tekst).'</p>';
?>
Mamy tutaj tekst zapisany w zmiennej (symulującej jakieś źródło danych, ponadto wykorzystamy go kilkakrotnie,
stąd ta zmienna) złożony z samych małych liter. Za pomocą funkcji strtoupper() zamieniamy je wszystkie na duże.
Odwrotną operację wykonuje strtolower(). ucfirst() zamienia na dużą literę początek pierwszego wyrazu ciągu, a
ucwords() - początek każdego wyrazu. Doświadczenie podpowiada nam, że otrzymywane dane nie zawsze są tak
klarowne. Jeżeli potrzebne nam automatyczne kapitalizowanie początków zdań, musimy sami napisać odpowiedni
algorytm.
Jeżeli chcemy wyciąć fragment jednego ciągu, aby np. poddać go bardziej szczegółowej obróbce, z pomocą
przychodzi nam substr(). Podajemy w niej pozycję, od której zamierzamy ciąć oraz ilość znaków do pobrania. W
przykładzie połączymy ją z funkcjami strpos() oraz strlen() (podaje długość ciągu) do wycięcia nazwiska z
personaliów pewnego człowieka.
<?php
$nazwa = 'Janusz Kowalski';
if(($id = strpos($nazwa, ' ')) !== false)
{
$nazwisko = substr($nazwa, $id, strlen($nazwa) - $id);
}
else
{
$nazwisko = '';
}
echo $nazwisko;
?>
W personaliach odnajdujemy spację, która posłuży nam jako marker. strpos() zwróci nam jej pozycję. Przekazujemy
ją do substr() jako punkt startowy wycinania i pobieramy ilość znaków równą długości personaliów minus pozycji
spacji. W ten sposób w nasze ręce wpadnie nazwisko i będziemy mogli zrobić z nim, co tylko chcemy. Alternatywny
sposób rozwiązania tego problemu polega na rozbiciu tego ciągu na tablicę poznaną już funkcją explode(). Przydaje
się on, kiedy oprócz nazwiska pragniemy otrzymać także drugi ciąg z imieniem - tu pasuje ona, jak znalazł.
Teraz coś bardziej praktycznego. Na wielu witrynach internetowych można spotkać emotikonki zamieniane na
obrazki. Jeżeli nie interesują nas żadne dodatkowe fajerwerki, napisanie konwertera jest bajecznie proste. PHP
posiada funkcję str_replace() zamieniającą jeden fragment ciągu na drugi (jej wariant, str_ireplace() nie rozróżnia
wielkości liter).
<?php
$post = 'Tak, zaiste jesteś bardzo zdolny :). Jeszcze nad praktyką popracuj :].';
// Pierwszy sposób zamiany
echo '<p>'.str_replace(':)', '<img src="smile.gif"/>', $post).'</p>';
// Drugi, kilka naraz
echo '<p>'.str_replace(array(
':)',
':]',
':('
), array(
'<img src="smile.gif"/>',
'<img src="eye.gif"/>',
'<img src="sad.gif"/>'
), $post);
?>
Pierwszym parametrem jest szukany ciąg, drugim - ciąg docelowy, a trzecim - ciąg, na którym operujemy. Możemy
zdefiniować tylko jeden wzorzec do podmiany albo całą grupę, którą podajemy w postaci tablic, jak na przykładzie.
Zwróć uwagę, że w przeciwieństwie do omawianego wcześniej substr(), tutaj ciąg, na którym operujemy,
wskazywany jest dopiero na końcu.
Miejsce podawania danych, na których funkcja ma operować, nie jest ujednolicone. Część
funkcji wymaga jego podania na końcu, a część na początku. Jest to pozostałość po
pionierskich czasach rozwoju PHP tworzonego wtedy po części metodą pospolitego ruszenia,
oraz ówczesnych inspiracjach językiem C.
Zajmując się księgą gości, dbaliśmy, aby do naszych wpisów nie przedostał się HTML. Funkcja htmlspecialchars()
zamieniała wtedy znaki specjalne HTML-a na encje, przez co nie mogły być one przetworzone i pojawiały się we
wpisie jako statyczny tekst. Okazuje się, że nie jest to jedyne dostępne nam rozwiązanie. Możemy znaczniki
wyrzucić całkowicie. Różne warianty prezentuje poniższy przykład:
<?php
$post = ' To jest post
kilkulinijkowy. Który należy przerobić tak,
aby go <b>fajnie wyświetlać</b>';
// Wariant 1
echo '<p>'.nl2br(htmlspecialchars(trim($post))).'</p>';
// Wariant 2
echo '<p>'.nl2br(strip_tags(trim($post))).'</p>';
?>
Zarówno strip_tags(), jak i trim() mają pewne ciekawe dodatkowe parametry, o których nie wszyscy wiedzą. Przy
wycinaniu znaczników możemy określić, które z nich mają być zostawiane, np. strip_tags($tekst,
'<b><i><u><a>') pozostawi nam pogrubianie, kursywę, podkreślanie oraz linki. trim() natomiast niekoniecznie
musi wycinać białe znaki - wystarczy, że podamy nasz własny zestaw jako drugi parametr, a możemy oszczędzić
sobie dużo pracy.
ASCII
Kiedy powstawały komputery, powstała konieczność stworzenia uniwersalnego standardu kodowania znaków
alfabetu za pomocą kodów liczbowych rozumianych przez maszyny. Umożliwiłoby to pisanie dokumentów, które da
się przenosić między maszynami bez konieczności ich konwersji. Tak narodził się siedmiobitowy standard ASCII
zawierający 128 kodów. Jego cechą szczególną było istnienie tzw. kodów sterujących (od 0 do 32), dzięki którym
można było nawet sterować pracą niektórych urządzeń. W latach późniejszych wykorzystano także ósmy bit (kody
od 128 do 255). Jego wykorzystanie różni się w zależności od wariantu systemu kodowania.
PHP udostępnia kilka funkcji ułatwiających pracę z ASCII. Na początku zapoznamy się z funkcjami chr() oraz ord()
konwertujących kod na odpowiadający mu znak i vice versa. Oto prosty skrypt , wykonujący to zadanie i
pobierający dane z formularza HTML.
<?php
if($_SERVER['REQUEST_METHOD'] == 'POST')
{ // 1
if($_POST['kierunek'] == ) // 2
{
if(strlen($_POST['dane']) != 1)
{
die('<p>Nieprawidłowe dane. Proszę podać JEDEN znak!');
}
echo '<p>Kod znaku <b>'.$_POST['dane'].'</b> to '.ord($_POST['dane']).'</p>';
}
else
{ // 3
if(!ctype_digit($_POST['dane']))
{
die('<p>Nieprawidłowe dane. Proszę podać kod znaku!');
}
echo '<p>Kodowi <b>'.$_POST['dane'].'</b> odpowiada znak '.chr($_POST['dane']).'</p>';
}
}
else
{ // 4
echo '<form method="post" action="ascii.php">
<h2>Informator ASCII</h2>
<select name="kierunek">
<option value="0">Sprawdź kod znaku</option>
<option value="1">Sprawdź znak pod kodem</option>
</select>
<input type="text" name="dane"/>
<input type="submit" value="OK"/>
</form>';
}
?>
Formularz przesyła nam dwie informacje:
Kierunek - określa, czy konwertujemy znak na kod, czy też kod na znak.
Dane - znak albo kod do zamiany.
Oto omówienie działania:
1. Dotarły do nas dane z formularza.
2. Konwersja ze znaku na kod. Sprawdzamy, czy pole dane zawiera dokładnie jeden znak. Jeśli tak, wyświetlamy
wynik.
3. Konwersja ze znaku na kod. Funkcją ctype_digit() sprawdzamy, czy wprowadzony kod składa się wyłącznie z
cyfr. Jeśli tak, wyświetlamy wynik.
4. Formularz do komunikacji ze skryptem.
W powyższym przykładzie wykorzystaliśmy funkcję ctype_digit(), która szybko bada, czy podany ciąg składa się
wyłącznie z liczb. Istnieje kilkanaście funkcji z serii ctype. Oto kilka z nich:
ctype_alpha($tekst) - sprawdza, czy tekst składa się wyłącznie z liter.
ctype_lower($tekst) - sprawdza, czy tekst składa się wyłącznie z małych liter.
ctype_upper($tekst) - sprawdza, czy tekst składa się wyłącznie z dużych liter.
ctype_xdigit($tekst) - sprawdza, czy tekst zawiera wyłącznie znaki do zapisu liczb w systemie szesnastkowym
(heksadecymalnym).
Jeśli żądany przez Ciebie zestaw znaków nie jest udostępniany przez żadną z funkcji, możesz skorzystać z
odpowiedniego algorytmu:
<?php
function ctype($ciag, $zestaw)
{
for($i = ; $i < strlen($ciag); $i++)
{
if(strpos($zestaw, $ciag{$i}) === FALSE)
{
return false;
}
}
return true;
} // end ctype();
?>
Algorytm jest bardzo prosty. Przeszukujemy wprowadzony ciąg znaków $ciag i sprawdzamy funkcją strpos(), czy
każdy jego znak zawiera się w zestawie $zestaw. Jeśli nie, przerywamy pracę i zwracamy false. Oto przykładowe
użycie:
echo ctype('555-1234', '0123456789-');
Jeżeli poszukujesz jeszcze bardziej złożonych związków między znakami, musisz posłużyć się wyrażeniami
regularnymi, z których podstawami zaznajomi Cię następny rozdział.
Formatowanie tekstu
Korzenie PHP znajdują się w języku C, więc nic dziwnego, że dziedziczy on po nim funkcję printf() służącą do
prezentacji sformatowanych danych.
<?php
printf('Liczba szesnastkowa: %x', 3342);
?>
Funkcja odnajduje w podanym ciągu specjalne kody formatujące rozpoczynające się od znaku procentu i umieszcza
na ich miejscu dane z kolejnych parametrów. W powyższym przykładzie zastosowaliśmy kod %x, który spowoduje
wyświetlenie się liczby 3342 w systemie szesnastkowym. Przeglądarka wyświetli zatem:
Liczba szesnastkowa: d0e
Za pomocą kodów formatujących możemy też np. określić precyzję wyświetlania ułamków. Aby wyświetlić liczbę π
do 4 miejsc po przecinku, napiszemy:
<?php
printf('Liczba PI: %0.4f', M_PI);
?>
To są jedynie podstawy kodów formatujących. Szczegółowy ich opis znajduje się w dokumentacji PHP. Warto
nadmienić także istnienie odmian funkcji printf(), np.
sprintf() - zwraca wynik jako ciąg tekstowy.
vprintf() - pobiera dane do kodów z tablicy przekazanej jako drugi parametr.
vsprintf() - pobiera dane do kodów z tablicy przekazanej drugim parametrem i zwraca wynik jako ciąg
tekstowy.
Kodowanie
Teraz nieco o przechowywaniu haseł użytkowników przez PHP. Generalnie nigdy nie składuje się ich w postaci
jawnej oraz nie koduje się algorytmem dwukierunkowym (czyli dającym teoretyczną możliwość ich rozszyfrowania).
Powszechna praktyka poleca stosowanie tzw. funkcji haszujących generujących unikalne, równej długości
sygnatury niszczące oryginalny przekaz, przez co nie da się ich już rozszyfrować metodą inną, niż sprawdzenie
wszystkiego na wszystkim. Wbrew początkowemu wrażeniu sens takiego działania jest bardzo oczywisty. Kiedy
użytkownik rejestruje się w serwisie, haszujemy jego hasło i zapisujemy w profilu. Próbując się zalogować, przysyła
nam swoje hasło jeszcze raz. Haszujemy je i wynik porównujemy z tym, co mamy w bazie. Jeżeli uzyskamy
identyczne hasze, znaczy to, że użytkownik podał hasło poprawnie i może zostać zalogowany.
PHP posiada wbudowanych kilka algorytmów haszujących:
crypt() - najstarszy, generuje 8-znakowe hasze. Nie polecamy do celów autoryzacji.
md5() - do niedawna najpopularniejszy algorytm. Generował 32-znakowe ciągi, lecz niedawno ujawniono w
nim poważne dziury.
sha1() - aktualnie najbezpieczniejszy algorytm haszujący dostępny domyślnie w PHP. Generuje 40-znakowe
ciągi.
Zwróć uwagę na jedną rzecz: hasze mają stałą długość, dlatego ilość możliwych kombinacji jest ograniczona.
Tymczasem możliwych ciągów jest nieskończenie wiele. Dlatego może się zdarzyć, że dwa różne ciągi generują ten
sam hasz i zwiemy to kolizją. W dobrym algorytmie, aby doszło do takiej sytuacji, muszą to być naprawdę różne
ciągi. Ponadto zmiana już pojedynczego znaku wewnątrz haszowanego ciągu musi powodować utworzenie zupełnie
innego wyniku. Siła algorytmu zależy od tego, jak dużo czasu potrzeba na wykrycie kolizji metodą brute-force, czyli
sprawdzenia wszystkiego na wszystkim. Algorytm MD5 został już złamany na tyle, że zwyczajny komputer PC jest
w stanie znaleźć kolizję już w ciągu zaledwie ośmiu godzin. Dla SHA1 ilość niezbędnych do sprawdzenia kombinacji
jest rzędu 2^64, z czym nie jest w stanie poradzić sobie żaden istniejący obecnie komputer.
Przypuśćmy, że $uzytkownicy jest tablicą asocjacyjną taką, że indeks jest nazwą użytkownika, a wartość hasłem
zaszyfrowanym w SHA1. Aby sprawdzić, czy użytkownik wpisał poprawne hasło, musimy wykonać następującą
czynność:
<?php
if(isset($uzytkownicy[$_POST['login']]) && $uzytkownicy[$_POST['login']] == sha1($_POST['haslo']))
{
echo 'Dziękujemy, podałeś dobre hasło.';
}
else
{
echo 'Nieprawidłowy login i/lub hasło.';
}
?>
Skonstruowanie systemu autoryzacji wymaga także zaimplementowania mechanizmu sesji, aby przekazać fakt
bycia zalogowanym między poszczególnymi żądaniami. Zajmiemy się tym w dalszych rozdziałach.
Treść pochodzi ze strony WikiBooks i jest udostępniana na licencji GNU FDL
Autor: WikiBooks
Artykuł pobrano ze strony eioba.pl