Vyhledání textu bez diakritiky

Školení, která pořádám

Osobně píšu všechny texty s diakritikou a vyhledávám tedy také s diakritikou. Uživatelé v zahraničí ale nemusí mít k dispozici českou klávesnici a zadávání znaků s diakritikou pro ně může být obtížné. Proto někdy může být užitečné upravit vyhledávání tak, aby vyhledávalo v textech bez diakritiky.

K tomuto cíli lze dospět několika různými způsoby. (Při způsobu porovnávání utf8_czech_ci se dá využít toho, že jediné znaky, které se při porovnávání liší od varianty bez diakritiky, jsou v souladu s českou normou pouze čřšž.)

Do databáze ukládat kromě originálního textu i variantu bez diakritiky a prohledávat v ní
Z ukládaného i hledaného textu odstraníme diakritiku a provedeme běžné porovnání.
Z prohledávaného textu odstranit diakritiku při vyhledávání
V MySQL není k dispozici obdoba funkce strtr, takže odstranění diakritiky lze provést leda několikanásobnou aplikací funkce REPLACE nebo uloženou funkcí. Vyhledávání by potom mělo podobu "WHERE REPLACE(REPLACE(REPLACE(REPLACE(clanek, 'č', 'c'), 'ř', 'r'), 'š', 's'), 'ž', 'z') LIKE '%$_GET[search]%'".
Vyhledávaný text převést na regulární výraz a vyhledat ho operátorem REGEXP
Vlastně jde o obdobu funkce sql_regcase. Vyhledávání potom může vypadat nějak takhle: "WHERE clanek REGEXP '" . strtr(quotemeta($_GET["search"]), array("c" => "[cč]", "r" => "[rř]", "s" => "[sš]", "z" => "[zž]")) . "'".
Využít toho, že při způsobu porovnávání utf8_general_ci jsou si znaky s diakritikou i bez ní rovny
Potom stačí hledanému textu tento způsob porovnávání nastavit: "WHERE clanek LIKE '%$_GET[search]%' COLLATE utf8_general_ci".

Při vyhledávání bychom také měli rozlišovat, jestli uživatel používá diakritiku nebo ne. Spolehlivou informaci nám dá pouze zaškrtávací políčko, kterým uživatel svou volbu vybere, to je ale uživatelsky poměrně nepříjemné. Proto je možné hledaný text zkontrolovat – když bude obsahovat znak s diakritikou, tak uživateli nemusíme místo např. každého z hledat i ž. Pokud je text bez diakritiky, tak uživatel buď hledá slovo bez háčků a čárek nebo diakritiku nepoužívá – v tom případě raději budeme počítat s druhou variantou.

Při vyhledávání s diakritikou můžeme zpřísnit i pravidla pro vyhledávání ostatních znaků – pokud uživatel hledá slovo „být“ nemusíme ho obtěžovat texty obsahujícími slovo „byt“. Jediné utf8 kódování, kde se rozlišují všechny znaky, je pouze utf8_bin, které bohužel rozlišuje velká a malá písmena, což je obvykle nežádoucí.

<?php
if (ereg("[\x80-\xFF]", $_GET["search"])) { // řetězec s diakritikou
    $where = "LOWER(clanek) LIKE LOWER('%" . mysql_real_escape_string($_GET["search"]) . "%') COLLATE utf8_bin";
} else { // řetězec bez diakritiky
    $where = "clanek LIKE '%" . mysql_real_escape_string($_GET["search"]) . "%' COLLATE utf8_general_ci";
}
?>
Jakub Vrána, Dobře míněné rady, 7.12.2007, diskuse: 16 (nové: 0)

Diskuse

Michal:

Osobne pro fulltext pouzivam SOLR (http://lucene.apache.org/solr). Lze nastavit tak aby hledal zaroven bez i s diakritikou, ale aby presne shode dal vetsi score. Pro anglictinu ma navic vyborny filter ktery zjisti koren slova a vyhledava podle nej, takze kdyz nekdo hleda "account" tak mu to najde i "accountant", coz se nekdy hodi. Bohuzel nevim jestli podobny filter existuje i pro cestinu.

ikona marek:

S tímhle jsem si teď začal hrát a vypadá to zajímavě....  Faktem je, že mě nikdy nenapadlo nic o problému hledání bez diakritiky - musím se ještě hodně učit :)

SiseL:

mne sa pre odstranenie diakritiky v MSSQL osvedcilo:
<stlpec> COLLATE SQL_Latin1_General_CP1251_CI_AS

Peter:

ja som si presne na toto napisal patch pre udf_charsets-1.0 od adelton-a ktory mi riesi konverziu utf8->ascii .
mysql> SELECT utf8_to_ascii("šušeň");
+---------------------------+
| utf8_to_ascii("šušeň")    |
+---------------------------+
| susen                     |
+---------------------------+
1 row in set (0.00 sec)

Michal Aichinger:

Jakube, čtu tě rád, ale tohle téma jsi docela odfláknul, resp. jdeš na to moc kostrbatě :-)

Běžně ukládáme data do utf8_czech_ci, tedy case insensitive a řazení je dle české normy. Ohledně otázky vyhledávání uvádím výtah z našeho firemního blogu:

Požadavky na fulltextové vyhledávání
Opět nám při nich zpravidla hraje důležitou roli abeceda, ovšem tentokrát nikoliv česká, ale anglická:
A B C D E F G H Ch I J K L M N O P Q R S T U V W X Y Z
Častým požadavkem na fulltextové vyhledávání totiž je, aby umělo hledat v textech bez ohledu jednak na velikost písmen (case insensitive) a jednak bez ohledu na diakritiku (accent insensitiv)....

...tak požadavkům na fulltextové vyhledávání vyhovuje utf8_general_ci. Jelikož jsme v poslední době řešili problém s accent insensitiv vyhledáváním na několika místech a to velmi kostrbatě, tak nyní pro jistotu polapaticky:
Pokud vyhledávání nad daty v utf v collation utf8_general_ci, tak jsou výsledkem dotazu:

SELECT * FROM clanky WHERE telo LIKE "%priroda%";

i články obsahující text "příroda".

No a na závěr, jelikož používáme výše zmíněné utf8_czech_ci kvuli řazení, které používáme obecně častěji, tak při vyhledávání provádíme konverzi na general takto:

SELECT * FROM clanky WHERE telo COLLATE utf8_general_ci LIKE "%priroda%";

Michal Aichinger:

mno asi jsem si to mel nejdriv poradne precist :-)

ikona Jakub Vrána OpenID:

Tak. Já čtenářům nerad něco podsouvám, takže raději řeknu nejen to, že je nějaké řešení nejlepší, ale zároveň i jaká jsou jiná řešení a čím jsou horší.

Vaclav Juchelka:

Lze to precollatovani pouzit i pro fulltext?

Robas:

dakujem.

honza:

Také používám konverzi COLLATE utf8_general_ci, ale lze to samé provést u fulltextu? Pokusy tomu nenasvědčují...

ikona Jakub Vrána OpenID:

Pokud vím, tak to skutečně nejde. Nezbývá než použít jiný popsaný přístup nebo u sloupce rezignovat na české řazení a nastavit mu utf8_general_ci přímo v tabulce (u souvislých textů si to můžeme dovolit, u nadpisů obvykle ne).

ikona radekzatec:

Všechny vás zdravím jsem začatečnik
prosim o pomoc, at delam co delam tak mi to vyhledáva jen slova s diaktrikou bez neumí.
Dále pri vyhledávani rozdeluje slova s velkým písmenem Kapr a kapr je jine slovo.
Nevim jak na to.

Hledal jsem v manualu a na různých forech i zde nasel jsem i texty venovane se tomuto tématu ale nenašel jsem rešení.

Proto jsem zacal hledat reseni a rekl jsem si že udelam prevod na ucfirst () - první velke písmeno fráze v proměné a strtolower () - celá fráze malými písmeny.

Problém je že fc ucfirst () mi převádí celou frázi velkými písmeny. Zkoušel jsem pres funkci substrs () odelit první pismeno a zvetsit jej a pak zase sloucit se zbyvajicim textem ale to mi zase niže uvedená funkce neumela udelat pismena s diaktritikou. delalo to toto  .

Prosím kohokoli kdo mi muže pomoci at poradi jak na to Jsem uplnej laik.

Samotnej kod funguje, ale funkce strtolower () na vic neumi napriklad preklad slova česnek (malíma písmenama) převede na slovo ĭesnek.
ucfirst () pri převodu slova Žába vznikne nyní toto ŝàBA (neumí prevest "Ž na ž"). Znáteli řesení meho problemu pomozte.

Klidne upravte kod jen nepiste udelej to  a to alespon blíže nastinte ukazkou kodu abych vedel o cem je řec moc díky.

Mam hosting na cizim serveru kde je

    * Verze MySQL: 4.1.15-Debian_0.dotdeb.4-log
    * Verze protokolu: 10
    * Server: MySQL 4.1 (SQL)
    * Znaková sada v MySQL: UTF-8 Unicode (utf8)

<p>
   <form action="vyhledavani.php" method="post">
      Slovo:<br />
      <input type="text" name="keywords" size="20" maxlength="40" value="" /><br />
      <input type="submit" value="Hledej!"/>
   </form>
</p>

<?php
function cz_ucfirst($keywords){
return
strtr($keywords,"abcdefghijklmnopqrstuvwxyzáäéěëíóöúůüýščřžďťň","ABCDEFGHIJKLMNOPQRSTUVWXYZÁÄÉĚËÍÓÖÚŮÜÝŠČŘŽĎŤŇ");
}
function
cz_strtolower($keywords){
return
strtr($keywords,"ABCDEFGHIJKLMNOPQRSTUVWXYZÁÄÉĚËÍÓÖÚŮÜÝŠČŘŽĎŤŇ","abcdefghijklmnopqrstuvwxyzáäéěëíóöúůüýščřžďťň");
}
   // Jestliže byl formulář odeslán s dodanými klíčovými slovy
   if (isset($_POST['keywords'])) {
     $keywords = $_POST['keywords'];
     $keywords1 = cz_ucfirst($keywords); //převede první písmeno na velké
     $keywords2 = cz_strtolower($keywords); //převede všechny písmena na malé
      //vytvoří dotaz
   $mysqldb->query("SELECT id, nazev_receptu  FROM kucharka  WHERE MATCH(nazev_receptu) AGAINST ('+(>$keywords1, $keywords2)' IN BOOLEAN MODE) OR MATCH(suroviny_k_priprave) AGAINST ('+(>$keywords1, $keywords2)' IN BOOLEAN MODE) ORDER BY nazev_receptu ASC ");

      // získá řádky vytvoří odkaz a nebo zobrazí vadnou zprávu
      if ($mysqldb->numrows() > 0) {
         while ($row = $mysqldb->fetchobject())
               echo "<a href=\"recept.php?id=$row->id\">$row->nazev_receptu</a><br />";

      } else {
         echo "Žádný výsledek.";
      }
   }
echo
"$keywords1 $keywords2."; // jen pro ověření že proměné splňují mé požadavky v samotném finalnim kodu již nebude.
?>

Fabian:

Diky za clanek. Pomohl

Chinese:

Zdar, měl bych dotaz.
Když uložím do databáze čínské nebo japonské znaky. MySQL je samo pro sebe předělává (vidím to v PHP MyAdminu).
Např. 教 změní na æ•™

Neví někdo, jak takto změnit znaky při vyhledávání?
Jako že kdby někdo zadal do hledání ten znak a převedl by se na æ•™. Protože když dám hledat æ•™ tak to MySQL samozřejmě najde.

Miško:

Chinese - tak ja mám nastavené všetko v mysql na utf general ci - a skúsil som do mojej aplikácie pár hiragana znakov naťukať a kuk do navicatu a všetko sa tam uložilo tak ako som to napísal - a v aplikácii PHP mi to tiež s DB vytiahlo a ukázalo tak ako som to napísal

Ivan Trnka:

Najprv som sa zlakol, ze co toto je, potom som si vsimol datum, ze 2007... takze OK.
Ale v roku 2014 by som tento typ vyhladavania nepouzil za ziadnych okolnosti. Velmi pomaly, bez relevancie...
Ak je to mozne, nepouzivajte mysql na fulltext. Ked ano, potom ulozte zaznamy duplicitne takym sposobom, ze vytvorite novu tabulku, ulozite do nej texty bez diakritiky a vysledky naparujte na realne data.
Nechce sa mi vypistovat, ako urobit konkretne vyhladavanie, detaily najdete tu
http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html

teda ziadny LIKE

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2018 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.