Obcování s ďáblem 20: Regulární výrazy se zpětnými odkazy - MujMAC.cz - Apple, Mac OS X, Apple iPod

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:

Soutěž

Sponzorem soutěže je:

IDIF

 

Jaký fotograf/ka získal/a cenu za nejpopulárnější příspěvek v Nikon Photo Contest?

V dnešní soutěži hrajeme o:

Seriály

Více seriálů



Software

Obcování s ďáblem 20: Regulární výrazy se zpětnými odkazy

6. prosince 2001, 00.00 | Dnes uzavřeme tuto část teorie o používání UNIXu. Poslední finta regulárních výrazů, jíž se budeme podrobněji věnovat, jsou zpětné odkazy.

Regulární výrazy se zpětnými odkazy

Poslední finta regulárních výrazů, jíž se budeme podrobněji věnovat, jsou zpětné odkazy.

Připomeňme jeden z příkladů, jež jsme si ukazovali na začátku jako problémy, jež lze s využitím regulárních výrazů snadno řešit:

"Představte si, že máte rozsáhlý projekt, ve kterém se v mnoha zdrojových souborech používá volání funkce "ellipse(x,y,r1,r2)" — kde samozřejmě x, y, r1 a r2 jsou obecné výrazy, určující střed elipsy a oba její poloměry. Na nových knihovnách se třeba objeví nová služba "circle(r,x,y)", která je mnohem efektivnější; chtěli bychom proto předělat všechna volání "ellipse" ve kterých je r1 stejné jako r2 na volání "circle" — nejenže musíme ověřit, zda je r1 a r2 stejné, ale navíc musíme změnit pořadí argumentů a jeden z nich vypustit."

Soustřeďme se zatím na vyhledání textu "ellipse(x,y,r1,r2)" ve zdrojových textech. Většinu už bychom toho uměli: na místě výrazů x, y, r1 a r2 prostě použijeme regulární výrazy ".*" (jistě, šlo by to i lépe, ale zatím si vystačíme s jednoduchým řešením). Výsledný regulární výraz by tedy mohl být "ellipse(.*,.*,.*,.*)"...

...ale ouha! Takovýto výraz nám najde i textové řetězce typu "ellipse(1,2,3,4)", a ty nechceme: zajímají nás pouze takové případy, kdy je r1 a r2 stejné. Co s tím? Je zřejmé, že potřebujeme zpětnou referenci typu "tady má být to samé, co je támhle".

Regulární výrazy naštěstí takového reference podporují; my se s nimi už vlastně jednou setkali, v příkladu "ls | egrep '^(.).*\1$'" ve dvanáctém dílu. Nyní se na ně podíváme podrobněji.

Zpětné odkazy

Součástí regulárních výrazů může být až devět zpětných odkazů. Samotný odkaz je representován prostě kombinací "\N", kde N je číslo odkazu 1-9; odkaz reprezentuje přesně stejný textový řetězec, který odpovídal regulárnímu výrazu uvnitř N-tého páru závorek.

Nyní si tedy již můžeme podrobně vysvětlit regulární výraz "^(.).*\1$", použitý ve dvanáctém dílu:

  1. "^": hned na začátku řádky...
  2. "(.)": ...je jeden znak (uzávorkování výrazu "." v tuto chvíli nehraje roli)...
  3. ".*": ...za nímž následuje cokoli...
  4. "\1": ...a pak je opět tentýž znak, se kterým jsme se setkali v bodě 2 (nyní se uzávorkování využije)...
  5. "$": ...a hned za ním už je konec řádku.

Už je asi také jasné, proč jsme zvolili egrep a ne grep: díky tomu, že egrep pracuje s rozšířenými regulárními výrazy, jsme si ušetřili několik zpětných lomítek. Příkaz grep by to se svými základními regulárními výrazy zvládl také, avšak odpovídající regulární výraz by byl o něco složitější: "^\(.\).*\1$" (kdo neví proč, ať si znovu přečte odstavec "Závorky" v minulém dílu!).

Popisovat řešení našeho problému s funkcí "ellipse" už je asi zbytečné — pozorní čtenáři už jistě vědí, jak na to. Proto jen pro kontrolu: zvolíme-li základní regulární výrazy, stačí pro vyhledání funkce, jež má oba poslední argumenty stejné, napsat "ellipse(.*,.*,\(.*\),\1)".

Záměny textových řetězců

Odkazy "\N" v regulárních výrazech se skvěle hodí i pro záměnu nalezeného textu jiným: často se nám stane, že uvnitř nového textového řetězce má zůstat nějaká část toho původního. S využitím klasického "find and replace" by to samozřejmě bylo nemožné; s regulárními výrazy není nic snazšího: stačí ve "vyhledávacím" výrazu požadovanou část uzávorkovat — a v textovém řetězci, jímž se nalezený text zaměňuje, použít odpovídající odkaz.

Pro příklad malinko předběhneme — ukážeme si řešení našeho problému (jímž je vyhledání funkcí "ellipse(x,y,r,r)" a jejich záměna funkcemi "circle(r,x,y)") s využitím standardního příkazu sed, který si podrobněji popíšeme až jindy. Pro dnešek se spokojíme s tím, že v podobě "sed -e '/<find>/s//<replace>/'" vyhledá výskyty regulárního výrazu "<find>" na standardním vstupu, a nahradí je textovým řetězcem "<replace>"; výsledek pak odešle na standardní výstup.

Nejprve si připravíme jednoduchá testovací data:

111 /tmp> cat > t
ellipse(1,2,3,4)
eclipse(1,2,3,3)
ellipse(5,6,7,7)
ellipse(moc,malo,argumentu)
ellipse(a,b+c,d-e+f*g,d-e+f*g)
112 /tmp>

Nyní použijeme příkaz sed s vhodnými regulárními výrazy pro hledání a záměnu:

112 /tmp> sed -e '/ellipse(\(.*,.*\),\(.*\),\2)/s//circle(\2,\1)/' < t
ellipse(1,2,3,4)
eclipse(1,2,3,3)
circle(7,5,6)
ellipse(moc,malo,argumentu)
circle(d-e+f*g,a,b+c)
113 /tmp>

Nejdřív se podíváme na vyhledávací regulární výraz, jímž je v příkazu sed řetězec "ellipse(\(.*,.*\),\(.*\),\2)". Už bychom mu měli rozumět: je to přesně to samé, čím jsme ukončili minulý odstavec, jen je navíc "zbytečně" uzávorkována část regulárního výrazu, jež odpovídá prvním dvěma parametrům.

Textový řetězec pro záměnu už je jednoduchý: je jím výraz "circle(\2,\1)", obsahující dva zpětné odkazy — první na poloměr (druhý pár závorek ve vyhledávacím výrazu), druhý na střed (druhý pár závorek).

To je pro dnešek vše

Příště se seznámíme trochu podrobněji s příkazy grep, fgrep a egrep, jež reprezentují asi nejpřímočařejší využití regulárních výrazů v unixu, a ukážeme si s nimi několik příkladů.

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » Informace  

 » Rubriky  » Agregator  

 » Rubriky  » Začínáme s  

 » Rubriky  » Software  

Poslat článek

Nyní máte možnost poslat odkaz článku svým přátelům:

Váš e-mail:

(Není povinný)

E-mail adresáta:

Odkaz článku:

Vzkaz:

Kontrola:

Do spodního pole opište z obrázku 5 znaků:

Kód pro ověření

 

 

 

 

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: