LINUXSOFT.cz Přeskoč levou lištu

ARCHIV



   

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

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ů

28.11.2018 23:56 /František Kučera
Prosincový sraz spolku OpenAlt se koná ve středu 5.12.2018 od 16:00 na adrese Zikova 1903/4, Praha 6. Tentokrát navštívíme organizaci CESNET. Na programu jsou dvě přednášky: Distribuované úložiště Ceph (Michal Strnad) a Plně šifrovaný disk na moderním systému (Ondřej Caletka). Následně se přesuneme do některé z nedalekých restaurací, kde budeme pokračovat v diskusi.
Komentářů: 1

12.11.2018 21:28 /Redakce Linuxsoft.cz
22. listopadu 2018 se koná v Praze na Karlově náměstí již pátý ročník konference s tématem Datová centra pro business, která nabídne odpovědi na aktuální a často řešené otázky: Jaké jsou aktuální trendy v oblasti datových center a jak je optimálně využít pro vlastní prospěch? Jak si zajistit odpovídající služby datových center? Podle jakých kritérií vybírat dodavatele služeb? Jak volit vhodné součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně datové centrum spravovat? Jak co nejlépe eliminovat možná rizika? apod. Příznivci LinuxSoftu mohou při registraci uplatnit kód LIN350, který jim přinese zvýhodněné vstupné s 50% slevou.
Přidat komentář

6.11.2018 2:04 /František Kučera
Říjnový pražský sraz spolku OpenAlt se koná v listopadu – již tento čtvrtek – 8. 11. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma umění a technologie, IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář

4.10.2018 21:30 /Ondřej Čečák
LinuxDays 2018 již tento víkend, registrace je otevřená.
Přidat komentář

18.9.2018 23:30 /František Kučera
Zářijový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 20. 9. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář

9.9.2018 14:15 /Redakce Linuxsoft.cz
20.9.2018 proběhne v pražském Kongresovém centru Vavruška konference Mobilní řešení pro business. Návštěvníci si vyslechnou mimo jiné přednášky na témata: Nejdůležitější aktuální trendy v oblasti mobilních technologií, správa a zabezpečení mobilních zařízení ve firmách, jak mobilně přistupovat k informačnímu systému firmy, kdy se vyplatí používat odolná mobilní zařízení nebo jak zabezpečit mobilní komunikaci.
Přidat komentář

12.8.2018 16:58 /František Kučera
Srpnový pražský sraz spolku OpenAlt se koná ve čtvrtek – 16. 8. 2018 od 19:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát jsou tématem srazu databáze prezentaci svého projektu si pro nás připravil Standa Dzik. Dále bude prostor, abychom probrali nápady na využití IoT a sítě The Things Network, případně další témata.
Přidat komentář

16.7.2018 1:05 /František Kučera
Červencový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 19. 7. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát bude přednáška na téma: automatizační nástroj Ansible, kterou si připravil Martin Vicián.
Přidat komentář

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

> Poslední diskuze

31.7.2023 14:13 / Linda Graham
iPhone Services

30.11.2022 9:32 / Kyle McDermott
Hosting download unavailable

13.12.2018 10:57 / Jan Mareš
Re: zavináč

2.12.2018 23:56 / František Kučera
Sraz

5.10.2018 17:12 / Jakub Kuljovsky
Re: Jaký kurz a software by jste doporučili pro začínajcího kodéra?

Více ...

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