Na navigaci | Klávesové zkratky

Translate to English… Ins Deutsche übersetzen…

Regulární korektura Intervalu.cz

Regulární výrazy jsou vynálezem amerického matematika Stephena Kleene a troufám si tvrdit, že jsou starší než většina čtenářů La Trine. Pro nezasvěcené působí jako tajemné formule s magickou silou. Zdrojů, ze kterých se dají vědomosti o výrazech získat, je dost. Od vyčerpávající nápovědy v PHP, přes články až po knihy.

Člověk omámený kouzlem regulárních výrazů často propadne jejich nadužívání. Proto je škoda, že neexistují i tituly jako „Deset případů, kde se regexp absolutně nehodí“ nebo „Zpověď programátora: Už týden jsem nepoužil regulární výraz“ :-)

Nebudu zde vysvětlovat principy regulárních výrazů – všechny zájemce bych rád odkázal na seriál Miroslava Pecky, který právě vychází na Intervalu. Protože jsme v něm narazil na určitou nesrovnalost, připojuji tuto svou korekturu.

Kde se vloudila chyba?

V části věnované subvýrazům a zpětným referencím autor uvádí, že zpětné reference se zapisují ve tvaru \\n, kde n je číslo subvýrazu. K tomu mám vážnou námitku: zapisují se ve tvaru \n.

Svá slova potvrzuji tímto příkladem. Jasně demonstruje, že při užití \2 \1 vše funguje dle očekávání, zatímco \\2 \\1 ke zdárnému cíli nevede. Tedy rozdíl v jednom lomítku je skutečně zásadní.

Poznámka: V regulárních výrazech nesmírně záleží na každém znaku, včetně mezer, a tudíž není možné tolerovat jakoukoliv nejasnost. Hledání chyb ve výrazech je mravenčí práce a může vést až k nervovým kolapsům včetně nepříjemného regurgitačního uvolnění organismu.

Něco tu nehraje

Co je zajímavé, příklady uvedené v článku Miroslava Pecky skutečně fungují, i s oním dvojitým lomítkem. Jak je to možné? Je to díky určité shodě okolností.

Každý programátor PHP by měl perfektně ovládat zápis řetězců. PHP k tomuto účelu nabízí hned tři způsoby, nejčastěji se používají tyto dva: uvození řetězce do jednoduchých a dvojitých uvozovek. Důležité je vědět, že takto zapsaný řetězec ještě PHP určitým způsobem transformuje do finální podoby.

V těchto transformacích hrají důležitou roli právě zpětná lomítka. Pohledem na příslušné místo v dokumentaci PHP zjistíte, že zápis "\50" podléhá transformaci (viz escapování) a PHP jej nahradí za znak s kódem 50 v osmičkové soustavě, což je mimochodem levá závorka. Naopak při použití jednoduchých uvozovek k transformaci nedojde:

echo "\50";   // vypíše (
echo "\\50";  // vypíše \50
echo '\50';   // vypíše \50

Z příkladu je vidět, že nechtěné transformaci se lze vyhnout zdvojením zpětného lomítka nebo (lépe) nahrazením dvojitých uvozovek za jednoduché.

A tím se dostáváme zpět ke kritizovaným zpětným referencím. Protože autor používá ve všech příkladech řetězce s dvojitými uvozovkami, musí zpětná lomítka zdvojovat. Tedy zdvojení si vynucuje vlastnost PHP, která s regulárními výrazy absolutně nesouvisí. Kdyby použil uvozovky jednoduché, zdvojovat lomítko není třeba (avšak nevadí to). Ve všech dalších případech (syntax heredoc, načítání z databáze nebo souborů, mnou uvedený příklad) už vadit bude a příklady přestanou fungovat.

Závěr: Doporučuji regulární výrazy zapisovat opatrně a nejlépe s použitím jednoduchých uvozovek.

Komentáře

  1. pif http://www.php-weblog.com #1

    avatar

    dalsi duvod proc pouzivat jednoduche uvozovky :))

    před 12 lety
  2. Jakub Vrána http://php.vrana.cz/ #2

    Kdyby použil uvozovky jednoduché, zdvojovat lomítko není třeba (avšak nevadí to).

    Já bych spíš napsal, že kdyby použil uvozovky jednoduché, tak by nevadilo, že lomítko není zdvojené.

    Je to spíše filozofický problém, ale \ slouží k escapování i uvnitř apostrofů. Kromě samotného \ totiž escapuje i apostrof. Když za lomítkem nenásleduje escapovaný znak, tak se to jako záchrana přeloží na \ + původní znak.

    Psát '\\1' je tedy podle mě správnější, protože skutečně chci vypsat zpětné lomítko a ne něco escapovat.

    Napsat, že zpětné reference se uvozují \\ je samozřejmě špatně, ale možná to pan Pecka napsal proto, aby začátečníkům nezamotal hlavu.

    před 12 lety
  3. spud #3

    Každý programátor PHP by měl perfektně ovládat zápis řetězců. PHP k tomuto účelu nabízí hned tři způsoby

    Sorry za offtopic, ale jaky je treti zpusob zapisu retezce v PHP? Nejak me nic jinyho nez uvozovky dvojity/jednoduchy nenapada.

    před 12 lety | reagoval [4] David Grudl
  4. David Grudl http://davidgrudl.com #4

    avatar

    #3 spude, Je s odkazem uveden na konci článku, hledej HEREDOC.

    před 12 lety
  5. Jakub Vrána http://php.vrana.cz/ #5

    Kdyby použil uvozovky jednoduché, zdvojovat lomítko není třeba

    Chudák ten, kdo si to vezme k srdci a bude chtít zkontrolovat např. Windows cestu a použije '~c:\\windows\\system~i' (vědom si toho, že \w a \s jsou v regulárním výrazu speciální znaky). Co se stane? Dvojitá lomítka požere už PHP a na regulární výraz zbyde \w a \s, což jsou zkratky za znak pro slovo a pro bílý znak.

    před 12 lety | reagoval [6] David Grudl [7] Jakub Vrána
  6. David Grudl http://davidgrudl.com #6

    avatar

    #5 Jakube Vráno, je třeba zapsat '~c:\\\\windows\\\\system~i', jinou cestu neznám. Ale nerozumím té narážce s chudákem a jeho srdcem.

    před 12 lety | reagoval [7] Jakub Vrána
  7. Jakub Vrána http://php.vrana.cz/ #7

    #6 Davide Grudle, To „to“ v #5 Jakub Vrána odkazuje na „Kdyby použil uvozovky jednoduché, zdvojovat lomítko není třeba“, tedy citovaný text. Možná jsi citovaný text přehlédl.

    před 12 lety
  8. David Grudl http://davidgrudl.com #8

    avatar

    Jakube, vím že v PHP jsi machr, nebude lepší celý problém vysvětlit začátečníkům, než se tu štengrovat mezi sebou?

    Ty napadáš větu, že v zdvojovat lomítko není třeba, která však souvisí se zápisem řetězce '\50'. A tady skutečně není třeba nic zdvojovat.

    před 12 lety
  9. spaze http://exploited.cz #9

    Bych to celé vyřešil tak, jak doporučují v manuálu u preg_replace:

    Replacement may contain references of the form \\n or
    (since PHP 4.0.4) $n, with the latter form being the
    preferred one.

    před 10 lety

Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.