Keresés és találatok kiemelése a szövegben

Kiemelő

A keresés megvalósítása még viszonylag egyszerű és kézenfekvő feladat annak, aki kicsit ért az SQL-hez. Feltéve persze, hogy a keresendő információt is SQL adatbázisban tárolja. De biztos mindenki látott már olyant, hogy a találatok ki voltak emelve valamilyen színes háttérrel. Erre írok egy nem tökéletes, de használható megoldást. Amiből már el lehet indulni egy komolyabb felé is.


A keresés még rendben van általában. De az ember szereti látni, hogy amire rákeresett, az mégis a talált szövegben hol van. Lehet persze a böngésző beépített kereső funkcióját is használni, ami firefox-ban a CTRL+F billentyű kombinációval is elérhető, de megspórolhatunk a felhasználónak ennyi többletmunkát.

Felvázolom az alap problémát.
Ha SQL adatbázist használunk, akkor amennyiben "_ci" végződésű karakterkészlettel rendelkező táblát használunk, a találatokban az "a", "A", "á" és "Á" is ugyanannak a karakternek számít. Ez jó is. Szerintem ezt az előnyt nem érdemes feláldozni. Tehát marad az a verzió, hogy a kijelölést is ennek megfelelően végezzük. Enélkül elég volna az str_replace() függvény használata. Most viszont a preg_replace() -re lesz szükség.

A megoldás első lépései:
Teszt szövegnek a már megszokott általános és értelmetlen kifejezést fogom használni, amiben benne van az összes magyar ékezetes karakter is. Nevezzük $msg -nek

$msg = "Árvíztűrő tükörfúrógép <b>ÁRVÍZ</b>TŰRŐ TÜKÖRFÚRÓGÉP";

Előfordulhat, hogy html is van a szövegben. Ezt benne hagyni bonyolultabb móka lenne, ezért a találatban minden html tag el lesz távolítva. És a puszta szöveg lesz megjelenítve a kiemeléssel. Nézzük hogy fog kinézni az űrlap. Mert hát az is kell.

<html>
<head>
<title>Kereső</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<style type="text/css">
body {
        background: #446699;
        color: white;
}
a {
        color: white;
}
</style>
</head>

<body>
<form action="" method="post">
Keresett kifejezés:
<input type="text" name="search" />
<input type="submit" value="Keres" />
</form>

Üzenet: <?php print $msg; ?>

</body>
</html>

A keresendő kifejezés tehát a $_POST['search'] változóban lesz. Viszont szerverbeállítástól függően lehet, hogy az idézőjelek backslashelve lesznek. Ezt a beállítást a get_magic_quotes_gpc() -vel kérdezhetjük le. És amennyiben be van kapcsolva, a stripslashes() függvénnyel törölhetjük a \ jeleket.

$search = $_POST['search'];
if (get_magic_quotes_gpc())
{
        $search = stripslashes($search);
}

A preg_replace hasonlóan az str_replace -hez 3 paramétert vár minimum. Azt, amit le akarunk cserélni, azt, amire cserélni akarjuk és azt, amiben. Az első két paraméter tömb is lehet. Akkor azonos sorrendben kell felsorolni a két tömbben amit és amire cserélni szeretnénk. Erre is szükség lesz, de előbb megmutatom milyen egyszerű is a kiemelés.

$msg = preg_replace('/'.$search.'/iu',"<span style='background-color: #000066;'>\\0</span>",strip_tags($msg));

A strip_tags függvény felelős a html tag-ek eltávolításáért. Így egy szimpla szövegben történik a kiemelés.Második paraméterben adjuk meg, mire kel cserélni a találatokat. Itt a \\0 jelenti a teljes mintát. Legyen elég a példához most ennyi. Akit érdekel, utánanézhet a függvénynek a php.net -en. Az első paraméter viszont érdekesebb. "/" jelek között van maga a keresendő minta. Utána az "i" módosító mondja meg, hogy a kis és nagybetűk különbsége ne számítson. Tehát a keresés itt is Case Sensitive lesz, mint az sql lekérdezésnél. Az "u" módosító az utf-8 karakterkódolás használata miatt kell. Mert másképp a unicode karakterekkel nem működne a függvény helyesen. Persze aki nem unicode -ot használ, elhagyhatja ezt a módosítót.

Igen ám, de ha a $search változóban megmarad a keresett kifejezés, akkor az ékezetes és nem ékezetes karakterek különbözni fognak, pedig adatbázisban ez nincs így. Ezért ennek a tartalmát is át kell konvertálni egy olyan mintára, ahol az egyes karaktereket karaktercsoportokká (atomokká) alakítjuk. Az atomok szögletes zárójelben vannak, és a bennük felsorolt karakterek úgymond vagy kapcsolatban fognak állni. Tehát a "találat" szóban ha az a és á betűket keressük, akkor így kellene megadni a mintát: [aá]
Ennek megfelelően tehát definiáljuk a karaktercsoportokat.

$to = array(
        '[aá]',
        '[eé]',
        '[oóöő]',
        '[ií]',
        '[uúüű]'
);

Külön sorba tettem minden csoportot az áttekinthetőség kedvéért. Az egy sorban levő karakterek a zárójelben így azonosnak fognak számítani. Ha valaki új karaktert szeretne egy csoporthoz adni, semmi akadálya. Ez volt az, amire cserélni kell. De Így a keresendő kifejezésben a magánhangzókat mintákra cseréljük. Azonban azt, hogy mely magánhangzókat cseréljük ezekre a mintákra, érdekes módon ugyanezzel a mintával kell definiálni. Csak kell köré a '/' jel és a unicode módosító.

$from = array_map(create_function('$v', 'return "/$v/u";'),$to);

Az array_map függvény első paramétere egy függvény neve. Vagy jelen esetben egy névtelen függvény, amit a create_funcion hoz létre. Ennek első paramétere a létrehozandó függvény paraméterlistája vesszővel elválasztva. Második paraméter pedig a függvény tartalma. Az aposztróf itt szándékos. Mert így a változókat nem próbálja meg kiértékelni az értelmező, hanem egyszerű szövegként kezeli. Végül az array_map második paramétere egy tömb, aminek minden elemét át kell adni az első paraméterben megadott függvénynek. Így lesz minden eleme a $from változónak "/$v/u" alakú, ahol $v egy-egy elem a $to tömbből. Aki ezt bonyolultnak látja, az adja meg nyugodtan kézzel:

$from = array(
        '/[aá]/u',
        '/[eé]/u',
        '/[oóöő]/u',
        '/[ií]/u',
        '/[uúüű]/u'
);

És most jön a végső pattern létrehozása.

$search =preg_replace($from,$to,preg_quote($search));

A preg_quote felel azért, hogy minden olyan karakter elé \ jelet tegyen a $search változóban, amik valamilyen mintát jelölnének. Enélkül például a [teszt] szóra keresésnél problémák lennének a kapcsos zárójelek miatt. Ekkor a $search változóban ha a "árvíz" szóra kerestem, a következő lesz: [aá]rv[ií]z

A teljes forrás tehát a következő:

<?php
$msg = "Árvíztűrő 'tükörfúrógép <b>ÁRVÍZ</b>TŰRŐ TÜKÖRFÚRÓGÉP";

if (isset($_POST['search']) and trim($_POST['search']) != '')
{
        $search = $_POST['search'];
        if (get_magic_quotes_gpc())
        {
              $search = stripslashes($search);
        }
        $to = array(
                '[aá]',
                '[eé]',
                '[oóöő]',
                '[ií]',
                '[uúüű]'
        );
 
        $from = array_map(create_function('$v', 'return "/$v/u";'),$to);
        $search =preg_replace($from,$to,preg_quote($search));
        $msg = preg_replace('/'.$search.'/iu',"<span style='background-color: #000066;'>\\0</span>",strip_tags($msg));
}
?>
<html>
<head>
<title>Kereső</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<style type="text/css">
body {
        background: #446699;
        color: white;
}
a {
        color: white;
}
</style>
</head>

<body>
<form action="" method="post">
Keresett kifejezés:
<input type="text" name="search" />
<input type="submit" value="Keres" />
</form>

Üzenet: <?php print $msg; ?>

</body>
</html>

A program kipróbálható: DEMO megtekintése
Egyúttal a forráskódra is található link a DEMO-ban is.

Kategóriák: 
Megosztás/Mentés

Hozzászólások

tbence képe

Valahonnét olyan ismerős. :D De még mindig jó.
Ha elköltözik az oldalam, majd nyáron beépítek egy keresőt belé, ugyanezt fogja használni. :)