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 34454×

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ů

23.5.2018 20:55 /Ondřej Čečák
Od pátku 25.5. proběhne na Fakultě informačních technologií ČVUT v Praze openSUSE Conference. Můžete se těšit na spostu zajímavých přednášek, workshopů a také na Release Party nového openSUSE leap 15.0. V na stejném místě proběhne v sobotu 26.5. i seminář o bezpečnosti CryptoFest.
Přidat komentář

20.5.2018 17:45 /Redakce Linuxsoft.cz
Ve čtvrtek 31. května 2018 připravuje webový magazín BusinessIT ve spolupráci s Best Online Média s.r.o. pátý ročník odborné konference Firemní informační systémy 2018. Akce proběhne v kongresovém centru Vavruška (palác Charitas), Karlovo náměstí 5, Praha 2 (u metra Karlovo náměstí) od 9:00 hod. dopoledne do cca 15 hod. odpoledne. Konference je zaměřena na efektivní využití firemních informačních systémů a na to, jak plně využít jejich potenciál. Podrobnější informace na webových stránkách konfrence.
Přidat komentář

14.5.2018 7:28 /František Kučera
Květnový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 17. 5. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát na téma: Audio – zvuk v GNU/Linuxu.
Přidat komentář

7.5.2018 16:20 /František Kučera
Na stránkách spolku OpenAlt vyšla fotoreportáž Pražské srazy 2017 dokumentující srazy za uplynulý rok. Květnový pražský sraz na téma audio se bude konat 17. 5. 2018 (místo a čas ještě upřesníme).
Přidat komentář

17.4.2018 0:46 /František Kučera
Dubnový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 19. 4. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tématem tohoto srazu bude OpenStreetMap (OSM) aneb svobodné mapy.
Přidat komentář

16.3.2018 22:01 /František Kučera
Kulatý OpenAlt sraz v Praze oslavíme klasicky: u limonády a piva! Přijďte si posedět, dát si dobré jídlo a vybrat z mnoha piv do restaurace Kulový blesk, který najdete v centru Prahy nedaleko metra I. P. Pavlova na adrese Sokolská 13, Praha 2. Sraz se koná ve čtvrtek 22. března a začínáme v 18:00. Heslo: OpenAlt. Vezměte s sebou svoje hračky! Uvítáme, když si s sebou na sraz vezmete svoje oblíbené hračky. Jestli máte nějaký drobný projekt postavený na Arduinu, nějakou zajímavou elektronickou součástku, či třeba i pěkný úlovek z crowdfundingové akce, neváhejte. Oslníte ostatní a o zábavu bude postaráno.
Přidat komentář

13.2.2018 0:41 /František Kučera
Únorový pražský sraz OpenAltu se koná 15. 2. 2018 a tentokrát se vydáme na návštěvu do jednoho pražského datacentra. Sejdeme se v 17:50 v severovýchodní části nástupiště tramvajové zastávky Koh-I-Noor. Po exkurzi se přesuneme do restaurace U Pštrosa (Moskevská 49), kde probereme tradiční témata (svobodný software a hardware, DIY, CNC, SDR, 3D tisk…) a tentokrát bude k vidění i IoT brána od The Things Network.
Přidat komentář

11.2.2018 23:11 /Petr Ježek
Hledáte lehký a rychlý prolížeč PDF souborů? Pokud vás již omrzelo čekat na načítání stránek či jiné nešvary, zkuste xreader.
Přidat komentář

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

> Poslední diskuze

20.2.2018 18:48 / Ivan Majer
portal

20.2.2018 15:57 / Jan Havel
Jak využíváte služby cloudu v podnikání?

16.1.2018 1:08 / Ivan Pittner
verejna ip od o2 ubuntu

15.1.2018 17:26 / Mira Harvalik
Re: Jak udělat HTML/Javascript swiping gallery do mobilu?

30.12.2017 20:16 / Michal Knoll
odmocnina

Více ...

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