Podřetězec v kódování UTF-8

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

PHP jak známo chápe řetězec jako posloupnost bajtů a nezohledňuje použité kódování. U dnes nejběžnějšího kódování UTF-8 jsou tedy funkce jako substr nebo strlen k ničemu. Alternativy pracující se znaky v různých kódováních jsou k dispozici v extenzích Iconv a MBstring. Pokud jsou tyto extenze k dispozici, tak je samozřejmě vhodné je použít. Co ale když k dispozici nejsou?

Kódování UTF-8 podporuje také funkce utf8_decode, která sice nedokáže pracovat se všemi znaky, pro samotné zjištění délky řetězce ale stačí. Pro získání podřetězce se dá využít toho, že s kódováním UTF-8 dokáže pracovat i extenze PCRE:

<?php
/** Vrátí podřetězec v kódování UTF-8
* @param string
* @param int
* @param int
* @return string
* @copyright Jakub Vrána, https://php.vrana.cz/
*/
function utf8_substr($string, $start, $length = null) {
    $chars = preg_split('~~u', $string, -1, PREG_SPLIT_NO_EMPTY);
    return implode(array_slice($chars, $start, $length));
}
?>

Dalo by se využít i konstrukce ~^.{10}(.{20})~su, to by ale chtělo trochu víc počítání (ale nakonec by to nejspíš bylo rychlejší a míň paměťově náročné). Pro složitější operace bychom si mohli napsat vlastní parser – kódování UTF-8 je sice poměrně jednoduché, ale ve většině případů to je zbytečná práce.

Při provádění řetězcových operací se dá také využít znalosti cílového prostředí. Pokud např. kontrolujeme délku řetězce nebo ho zkracujeme proto, abychom ho poslali do MySQL, tak si můžeme ušetřit práci – MySQL ve striktním režimu odmítne vložit příliš dlouhá data (takže za nás provede validaci), mimo striktní režim je ořízne (takže za nás provede sanitizaci). U formulářů ukládaných do MySQL se dá tedy použít kombinace maxlength (dobrovolná validace) a poslání do databáze (povinná sanitizace) – validaci i sanitizaci na úrovni PHP lze zcela vynechat.

Jakub Vrána, Řešení problému, 15.6.2011, diskuse: 5 (nové: 0)

Diskuse

ikona Fruiko:

Dlouhodobě používám na jednoduché ořezávání něco podobného:

<?php
function utf_substr($str,$start){
   preg_match_all("/./su", $str, $ar);
   if(func_num_args() >= 3) {
       $end = func_get_arg(2);
       return join("",array_slice($ar[0],$start,$end));
   } else {
       return join("",array_slice($ar[0],$start));
   }
}
?>

Martin Hruška:

Není už pomalu čas, aby čeští programátoři začali opravadu programovat a přestali řešit jak nahradit funkce, které muj socko hosting za 10 korun ročně neumí?

Takhle nám ten svět ujede ještě víc.

ikona Jakub Vrána OpenID:

Vůbec nejde o socko hostingy. U některých knihoven a aplikací je prostě výhodné mít co nejmíň závislostí. A ne jen proto, aby fungovaly pokud možno všude, ale i proto, že to je prostě levnější. Podpora totiž pak nemusí řešit dotazy typu „Vaše aplikaci mi píše, že potřebuje extenzi Iconv. Co mám dělat?“

aa:

Myslím, že obava, že by funkce iconv_substr nebyla k dispozici, je v dnešní době, kdy je iconv už řadu let základní součástí PHP, zbytečná.

ikona David Grudl:

Soudě podle Nette fóra, hostingy bez iconv jsou poměrně vzácné, podobně vzácně se dá ale narazit i na hostingy bez nebo s neúplnou podporou UTF-8 v regulárních výrazech. Raritou (nějaký server na MUNI) byl stroj s nefunkční iconv - funkce byly přítomny, ale vracely prázdné řetězce.

Jinak substr je v UTF-8 docela užitečná, je to chce použít ty správné indexy, např. substr($utfStr, 0, strpos($utfStr, $utfSubStr))

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 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.