LINUXSOFT.cz Přeskoč levou lištu
Uživatel: Heslo:  
   CZUKPL

> Perl (20) - Regulární výrazy - magické závorky

Dnešní díl popisuje použití kulatých závorek pro změnu priority, seskupování a pamatování.

5.12.2005 06:00 | Jiří Václavík | Články autora | přečteno 33814×

V regulárních výrazech mají závorky hned několik důležitých a jinak nenahraditelných významů. Mezi jejich hlavní funkce patří seskupení, priorita a paměť. Rozeberme si postupně tyto tři vlastnosti.

Priorita

Stejně jako pro operátory existuje tabulka priorit i pro metaznaky. Určuje, ke kterým znakům se váže určitý metaznak. Čím výše v tabulce, tím je metaznak prioritnější.

MetaznakyVýznam
( ), (?: )závorky
*, ?, +, {}, *?, ??, +?, {}?kvantifikátory
aa, \x, ^, $ apod.posloupnost znaků, kotvy
|alternativy

Priorita se dá měnit kulatými závorkami.

Seskupování

Kvantifikátor jsme zatím používali pouze pro znak nebo třídu znaků, která ho předcházela. Pomocí závorek můžeme docílit toho, aby se vztahoval na nějakou delší část regulárního výrazu. Srovnejme si tyto 2 vzory:

  /(xy){3}/;
  /xy{3}/;

V 1. případě jsou znaky xy seskupeny. To znamená, že se kvantifikátor vztahuje na celý řetězec xy v závorkách. Lze to vysvětlit také jako změnu priority - kvantifikátor má menší prioritu než závorky a proto je aplikován na celý uzávorkovaný výraz. Takovému regulárnímu výrazu vyhoví řetězec, který obsahuje xyxyxy. Další řádek je stejný až na to, že chybí závorky a znaky xy seskupeny nejsou. Kvantifikátor je prioritnější než posloupnost znaků a vztahuje se pouze na znak y. Vzoru tedy vyhoví xyyy.

Pamatování obsahu závorek

Vše, co uzávorkujete kulatými závorkami si Perl, pokud mu to nezakážete, pamatuje. To je vlastnost, se kterou lze dělat opravdu nevídané věci.

Výraz $text =~ /(...)/ vrací úsek textu, který vyhověl regulárnímu výrazu. Tímto způsobem můžeme do pole velice pohodlně získat třeba seznam slov nebo čísel. Právě seřazený výtah čísel ze zadaného textu tiskne následující ukázka.

  $, = ", ";
  $text = "333 stříbrných stříkaček stříkalo přes 33 stříbných střech";
  @cisla = ($text =~ /(\d+)/g);
  print sort @cisla; #tiskne 33, 333

Obsah závorky se ukládá okamžitě a takto uložený řetězec se dá použít ještě ve vzoru. To, co je v 1. závorce je přístupné v \1, další závorka \2 a tak dále až do \99 (dále ne, protože by se takový zápis překrýval s osmičkovým zápisem znaku. \100 je zavináč, \101 je znak A apod.). Tento mechanizmus nám otevírá spoustu dalších možností.

Pokusme se zapsat vzor, který vyhoví řetězci o 5 stejných číslicích. Jinými slovy vzor, kterému, pokud začíná na 1, vyhoví pouze 11111, pokud začíná na 2, vyhoví pouze 22222, atd. Pokud vzor nezačíná číslicí, nevyhoví nikdy.

Jako 1. znak hledáme číslici. Zápis množiny číslic musí být v závorkách, abychom její hodnou neztratili. Dále použijeme zapamatovanou hodnotu jako množinu znaků a otestujeme ji 4x. Nakonec označíme začátek a konec řetězce.

  print "MATCHED" if "55555" =~ /^(\d)\1{4}$/; #vyhovuje
  print "MATCHED" if "555555" =~ /^(\d)\1{4}$/;#nevyhovuje
  print "MATCHED" if "11112" =~ /^(\d)\1{4}$/; #nevyhovuje
  print "MATCHED" if "xxxxx" =~ /^(\d)\1{4}$/; #nevyhovuje

Tato vlastnost se hodí zejména při nahrazování, které bude rozebráno v příštím díle.

Podobně lze pomocí závorek získávat hodnoty i mimo samotný vzor. Obsahy jsou přístupné ve speciálních proměnných $1, $2$99. Nyní z řetězce opět vyseparujeme všechna čísla. Použijeme cyklus, každou iteraci bude 1 číslo zapamatováno a v těle cyklu vytištěno.

  while ("333 stříbrných stříkaček stříkalo přes 33 stříbných střech" =~ /(\d+)/g){
      print "Číslo: $1\n";
  }

Pořadí (u zápisu \n i $n) se uděluje na základě pozice otevírací závorky. Tento systém řeší případné víceúrovňové zanoření. Nějaké podřetězce tak mohou být uloženy i vícekrát. Nic tedy nebrání například zápisu ((\d)) - ta stejná číslice bude uložena do více proměnných. Zanořování si ilustrujeme na následujícím úseku kódu. Ten vypíše první a poslední číslici nejméně trojmístného zadaného čísla a také toto celé číslo.

  <STDIN> =~ /^((\d)\d+(\d))$/;
  print "1. číslice: $2\n";
  print "poslední číslice: $3\n";
  print "celé číslo: $1\n";

Poznámka - Pokud řetězec nebude vyhovovat vzoru, proměnné zůstanou prázdné - obsah závorek se ukládá, jen pokud byl test vyhodnocen pravdivě. Problémy pak mohou nastat, pokud v proměnných $1, $2 atd. zbyly nějaké hodnoty z předchozího testu. Jde o velice nenápadnou chybu a může trvat dlouho, než je odhalena.

Poznámka - Jaký je tedy rozdíl mezi \n i $n? \n je do paměti ukládáno okamžitě s uzavírací závorkou, takže je možné tímto způsobem zapamatovanou hodnotu použít ještě ve vzoru. Oproti tomu proměnné $n se načtou vždy až po úspěšném srovnání řetězce se vzorem.

Ukládání do \n, resp. $n lze zabránit speciální syntaxí pro zápis závorek. Místo levé (otevírací) závorky použijte posloupnost znaků (?:. V takovém případě se nic ukládat nebude a proměnná $n se bude chovat jako ostatní nedefinované proměnné, což dokazuje kód:

  "123456789" =~ /^(?:\d*)$/;
  print "Hodnota uložena\n" if defined $1;

Vždy, když není pamatování potřeba, je lepší použít právě verzi bez zapamatování. Zvyšuje se tím rychlost vyhodnocení regulárního výrazu.

Hezky lze také využít pamatování v souvislosti s kontextem. Tato vlastnost umožňuje například snadno vyseparovat z rodného čísla den, měsíc a rok narození. Stačí jen uzavřít příslušné části regulárního výrazu do závorek a regulární výraz přiřadit do nějakého seznamu.

  $rc = "3012013522";
  ($rok, $mesic, $den) = $rc =~ /^(\d\d)(\d\d)(\d\d)\d{4}$/;
  print "Narozen $den.$mesic.19$rok.";

Poznámka - Příklad je samozřejmě zjednodušený - nebere v úvahu ženská rodná čísla a narozené jindy než mezi roky 1900 a 1999.

Nejdříve se vyhodnotil pravý operand přiřazení. Tak jsme získali seznam, který jsme přiřadili do už pojmenovaného seznamu. Problém opět nastává, pokud srovnávaný řetězec nevyhoví regulárnímu výrazu. Lze to však řešit například umístěním podmínky za příkaz.

  print "Narozen $den.$mesic.19$rok." if ($rok, $mesic, $den) = $rc =~ /^(\d\d)(\d\d)(\d\d)\d{4}$/;

Speciální proměnné

V proměnné $& je část testovaného řetězce, která se shoduje se vzorem. Tímto způsobem si můžeme mimo jiné hezky ilustrovat hladovost kvantifikátorů.

  "číslo 101 je prvočíslo" =~ /\d+.{2,10}/;
  print $&;
  
  "číslo 101 je prvočíslo" =~ /\d+.{2,10}?/;
  print $&;

1. zápis je hladový. Spolkne 10 znaků za číslem 101: "101 je prvočí". Pokud za kvantifikátor přidáme otazník, stává se sytým a pohltí pouze nejmenší možný počet znaků, které vyhoví: "101 j".

Další proměnné $` a $' tisknou tu část testovaného řetězce, kterou nevytiskla proměnná $&. $` tiskne část před ("číslo ") a $' část za ("e prvočíslo").

  "číslo 101 je prvočíslo" =~ /\d+.{2,10}?/;
  print $`;
  print $';

V proměnné $+ je uložen poslední závorkami zapamatovaný řetězec.

Poznámka - Je dobré mít na paměti, že proměnné $&, $`, $' a $+ značně zpomalují program.

Speciální proměnná @- si pamatuje v prvku s indexem 0 první pozici v testovaném řetězci, která vyhovuje vzoru. V dalších prvcích pak pozice začátků zapamatovaných podřetězců. Proměnná @+ dělá to samé, ale pamatuje si místo začátků konce.

Funkce split a regulární výrazy

Oddělovač, který je parametrem této funkce, lze uvést i jako regulární výraz. To je v mnoha případech opravdu užitečné. Mějme nějaký text, ze kterého načteme do pole seznam slov.

  $, = "\n";
  @pole = split (/\W+/, "Text, ve kterém je interpunkce!");
  print @pole;

Vše mezi slovy (tedy oddělovače - mezery a interpunkce - lépe řečeno vše, co vyhovuje vzoru) je navždy ztraceno. Být tomu tak vždy nemusí. Proč, to si budeme demonstrovat na jiném příkladu. Budeme mít nějaký řetězec, ve kterém jsou promíchány čísla a právě čísla zvolíme jako oddělovač.

  $, = " "; $\ = "\n";
  print split (/\d+/, "abc01def2ghijk34567l89mnop");
  print split (/(\d+)/, "abc01def2ghijk34567l89mnop");
  print split (/(?:\d+)/, "abc01def2ghijk34567l89mnop");

V 1. případě se vytiskne seznam získaných podřetězců tak, jak jsme to dosud znali. Zajímavé to začíná být až na dalším řádku. Vzor je uveden v závorkách, které zde mají ten efekt, že se do výsledného seznamu tisknou i oddělovače! Skutečným oddělovačem je tedy (v tomto konkrétním případě) hranice mezi podřetězcem vyhovujícím vzoru a jiným znakem. Nic z původního řetězce tak není ztraceno. Posledním případem je speciální druh uzávorkování, které této vlastnosti zamezuje - oddělovače se chovají jako v 1. případě.

  $ perl split.pl
  abc def ghijk l mnop
  abc 01 def 2 ghijk 34567 l 89 mnop
  abc def ghijk l mnop
  $

Je též možné ozávorkovat pouze část vzoru. V tom případě bude do výsledného seznamu přiřazena příslušná část oddělovače.

  print split (/(\d+)45/, "***12345***12345***123***12345678***");

Podtržené podřetězce ukazují oddělovače. Ve výsledném seznamu ale bude jejich část (znaky 45) odmazána, protože jsou mimo závorky (částí oddělovače ale samozřejmě zůstávají).

Dále můžete také vyzkoušet závorky různě vnořovat, zdvojovat apod. V takových případech budou ve výsledném seznamu všechny uzávorkované řetězce. A to i přesto, že některé části pak budou ve výsledném seznamu vícekrát.

Verze pro tisk

pridej.cz

 

DISKUZE

Nejsou žádné diskuzní příspěvky u dané položky.



Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

15.5.2017 23:50 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt, který se bude konat ve čtvrtek 18. května od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).
Přidat komentář

12.5.2017 16:42 /Honza Javorek
PyCon CZ, česká konference o programovacím jazyce Python, se po dvou úspěšných ročnících v Brně bude letos konat v Praze, a to 8. až 10. června. Na konferenci letos zavítá např. i Armin Ronacher, známý především jako autor frameworku Flask, šablon Jinja2/Twig, a dalších projektů. Těšit se můžete na přednášky o datové analytice, tvorbě webu, testování, tvorbě API, učení a mentorování programování, přednášky o rozvoji komunity, o použití Pythonu ve vědě nebo k ovládání nejrůznějších zařízení (MicroPython). Na vlastní prsty si můžete na workshopech vyzkoušet postavit Pythonem ovládaného robota, naučit se učit šestileté děti programovat, efektivně testovat nebo si v Pythonu pohrát s kartografickým materiálem. Kupujte lístky, dokud jsou.
Přidat komentář

2.5.2017 9:20 /Eva Rázgová
Putovní konference československé Drupal komunity "DrupalCamp Československo" se tentokrát koná 27. 5.2017 na VUT FIT v Brně. Můžete načerpat a vyměnit si zkušenosti z oblasti Drupalu 7 a 8, UX, SEO, managementu týmového vývoje, využití Dockeru pro Drupal a dalších. Vítáni jsou nováčci i experti. Akci pořádají Slovenská Drupal Asociácia a česká Asociace pro Drupal. Registrace na webu .
Přidat komentář

1.5.2017 20:31 /Pavel `Goldenfish' Kysilka
PR: 25.5.2017 proběhne v Praze konference na téma Firemní informační systémy. Hlavními tématy jsou: Informační systémy s vlastní inteligencí, efektivní práce s dokumenty, mobilní přístup k datům nebo využívání cloudu.
Přidat komentář

15.4.2017 15:20 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě IoT a radiokomunikace? Přijď na sraz spolku OpenAlt, který se bude konat ve středu 19. dubna od 18:30 v Šenkovně (Sokolská 60, Praha 2).
Přidat komentář

5.3.2017 19:12 /Redakce Linuxsoft.cz
PR: 23. března proběhne v Praze konferenci na téma Cloud computing v praxi. Hlavními tématy jsou: Nejžhavější trendy v oblasti cloudu a cloudových řešení, Moderní cloudové služby, Infrastruktura současných cloudů, Efektivní využití cloudu, Nástrahy cloudových řešení a jak se jim vyhnout.
Přidat komentář

27.2.2017 22:12 /František Kučera
Pozvánka na 137. sraz OpenAlt – Praha: Tentokrát jsme si pro vás připravili neobvyklou akci. Ve středu 1.3. v 17:30 nás přivítá sdružení CZ.NIC ve svých prostorách v Milešovské ulici číslo 5 na Praze 3, kde si pro nás připravili krátkou prezentaci jejich činnosti. Následně navštívíme jejich datacentrum pod Žižkovskou věží. Provedou nás prostory, které jsou běžnému smrtelníkovi nedostupné!
Po ukončení prohlídky se všchni odebereme do hostince U vodoucha, Jagelonská 21, Praha 3 pochutnat si na některém z vybraných piv či dát si něco na zub. Rezervaci máme od 19:30, heslo je OpenAlt.
Ale pozor! Do prostor datového centra máme omezený přístup, dostane se tam pouze 10 lidí! Takže kdo přijde dříve, ten má přednost, a občanky s sebou! Kdo nebude chtít na prohlídku datového centra, může se pomalu přesunout do hostince U vodoucha a u nepřeberné nabídky piv počkat na ostatní.
Přidat komentář

18.1.2017 0:49 /František Kučera
Členové a příznivci spolku OpenAlt se pravidelně schází v Praze a Brně. Fotky z pražských srazů za uplynulý rok si můžete prohlédnout na stránkách spolku. Příští sraz se koná už 19. ledna – tentokrát je tématem ergonomie ovládání počítače – tzn. klávesnice, myši a další zařízení. Také budete mít příležitost si prohlédnout pražský hackerspace Brmlab.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

1.8.2017 7:32 / Cassidy
structural consultants

30.7.2017 11:12 / Jaromir Obr
Národní znaky

27.7.2017 12:24 / Jaromir Obr
Cteni/zapis

26.7.2017 21:12 / Jaromir Obr
Podminka

15.6.2017 9:34 / Ondřej Havlas
php,

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2017) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze