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

> Perl (24) - Regulární výrazy - příklady

Náš miniseriál se pomalu chýlí ke konci. Předposlední díl bude ryze praktický.

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

Již znáte řadu konstrukcí, které se v regulárních výrazech používají, ale jedna důležitá věc stále chybí. Dosud zde byly regulární výrazy podány výhradně teoreticky. Dnes se to změní, protože celý díl je věnován čistě příkladům.

Testování vstupu

To, že je třeba každý vstup z neověřeného zdroje testovat, je jasné. A právě regulární výrazy nabízejí spolehlivé a pohodlné řešení.

Mechanizmus takového testování si ukážeme. Budeme chtít po uživateli zadat trojciferné číslo a vzápětí zkontrolujeme, zda ho opravdu zadal. Na takové drobnosti se regulární výrazy využívají velmi často.

  print "Zadej trojciferné číslo: ";
  $cislo = <STDIN>;
  if ($cislo =~ /^[1-9]\d{2}$/){
      print "OK\n";
  }else{
      die "Chyba! Toto není trojciferné číslo!\n";
  }

Validace emailové adresy

Vyjádření emailové adresa je další regulární výraz, který patří k těm nejčastějším. Dokonalý zápis je velice složitý. Jeho vytvoření je popsáno v knize Mastering Regular Expressions. My se spokojíme s poměrně jednoduchým zápisem. Před zavináčem povolíme alfanumerické znaky a dále tečku a pomlčku. Část za zavináčem se skládá ze 2 částí. Doménu lze vyjádřit stejně jako jméno před zavináčem a přípona se skládá ze 2-4 písmen. Uvádím pouze samotný regulární výraz:

  /^[\w\.-]+@[\w\.-]+\.[a-z]{2,4}$/i

Hledání odkazů v HTML souboru

Máme HTML soubor. Z něj chceme získat vše, co je mezi <a href=" a "> a mezi <a...> a </a>. Tedy odkaz a jeho popis. Získaná data vytiskneme.

Budeme postupně načítat řádky souboru (nebudeme brát ohled na odkazy obsahující znak nového řádku). V každém řádku se pokusíme najít HTML odkaz, z něj zapamatujeme adresu a popis a vytiskneme. Protože mohou existovat řádky bez odkazu, je nutné tisknout pouze v případě, že porovnání vzoru bylo úspěšné. Toho dosáhneme podmíněnním příkazu print. Dále víme, že HTML není case sensitive. Proto přidáme přepínač i.

  open SOUBOR, "index.html" or die "Nelze otevřít soubor. $!";
  while (<SOUBOR>){
      print "$1 => $2\n" if $_ =~ /
          <A\sHREF=
          ["']       #uvozovky nebo apostrofy
          ([^"']*)   #vše mimo uvozovek nebo apostrofů
          ["']       #koncová uvozovka nebo apostrof
          >
          ([^<]*)    #vše mezi <a href...> a <\/a>
          <\/A>
      /ixg;
  }

Vyvstává nám tu několik problémů. Co když je na 1 řádku více odkazů? Na každý řádek se totiž aplikuje regulární výraz pouze jednou, není proto šance najít více než 1 odkaz. Problém by vyřešil další cyklus. Není ale potřeba nic zásadně upravovat. Pouze if zaměníme za while.

Dalším problémem jakým způsobem vyhoví řetězce jako <a href="xxx'...> Jako odkaz se vyseparuje pouze xxx. Jinými slovy musíme zajistit, aby byly oba uvozující znaky stejné. To je úloha jako šitá pro pamatování. Uzavřeme tedy úvodní uvozovací znak do závorek (['"]) a místo dalšího ['"] pro uzavření jen \1. Podobně upravíme část ([^"']*), kde nahradíme znaky "' za \1.

Po provedení úprav získáváme již funkční program:

  open SOUBOR, "index.html" or die "Nelze otevřít soubor. $!";
  while (<SOUBOR>){
      print "$2 => $3\n" while $_ =~ /
          <A\sHREF=
          (["'])     #uvozovky nebo apostrofy
          ([^\1]*?)  #vše mimo uvozujícího znaku
          \1         #koncová uvozovka nebo apostrof, podle toho, který znak uvozuje
          >
          ([^<]*)    #vše mezi <a href...> a <\/a>
          <\/A>
      /ixg;
  }

Zvýraznění skalárních proměnných

Vytvoříme nástroj, který přijímá jako parametr soubor, ve kterém zvýrazní všechny skalární proměnné. Bude-li se například vyskytovat v textu řetězec '$promenna', bude nahrazen za '<font color="#800000">$promenna</font>'.

Každou iterací cyklu načteme řádek zdrojového kódu Perlu, v něm zvýrazníme výskyty proměnných a vytiskneme výsledek. Jediný problém tak spočívá ve vytvoření vzoru, kterému vyhoví identifikátor skalární proměnné. Vzor bude začínat dolarem, následuje libovolné písmeno nebo podtržítko (nebereme ohled na speciální proměnné) a nakonec libovolný počet znaků slova. Celý vzor uzavřeme do kulatých závorek, získáme tak proměnnou $1, kterou použijeme jako část náhrady.

  while (<>){
      s/(\$[_a-zA-Z]{1}[\w_]*)/<font color="800000">$1<\/font>/g;
      print;
  }

Získání adresáře a jména souboru z umístění

Řetězec, který je cestou k souboru, rozdělíme na 2 části. Na adresář, ve kterém je daný soubor a jeho relativní jméno. Víme, že oddělovacím znakem je poslední lomítko. Využijeme tak hladovosti kvantifikátorů.

  $umisteni = "/boot/grub/menu.lst";
  ($adresar, $soubor) = $umisteni =~ /^(.*\/)(.*)$/;
  print "Adresář: $adresar\nSoubor: $soubor\n";

Výpočet výrazů v textu

Na vstupu přijme program textový řetězec, ve kterém se občas může vyskytnout podřetězec <<výraz>>. Výraz vyhodnotíme a získanou hodnotu za něj nahradíme.

Budeme tedy postupně načítat řádky textu a v něm takové výrazy hledat. Na 1 řádku se může vyskytnout více výrazů, proto použijeme přepínač g. Další přepínač, který aplikujeme, je e. Umožňuje nám přímo v regulárním výrazu nahrazovat za výsledek nějakého výrazu. A použijeme ho hned 2krát, protože ještě potřebujeme vyhodnotit řetězec, jako by byl částí zdrojového kódu.

  while (<>){
      s/<<([^>]+)>>/$1/gee;
      print;
  }

Spíše jen pro ukázku zde uvádím zápis stejného programu, který používá pouze jeden přepínač e. Funkce eval může ten druhý zastoupit.

  while (<>){
      s/<<([^>]+)>>/eval $1/ge;
      print;
  }

Pošleme programu na vstup řetězec

  Týden má <<7>> dní, <<7*24>> hodin, <<7*24*60>> minut nebo <<7*24*60*60>> sekund.

a získáme

  Týden má 7 dní, 168 hodin, 10080 minut nebo 604800 sekund.

Upozorňuji, že toto je jen ilustrační příklad na regulární výrazy. Mimo jiné je totiž také velkou bezpečnostní dírou. Umožňuje spuštění příkazů. Například po zadání řetězce "...<<system("rm soubor")>>..." bude proveden systémový příkaz rm soubor. Speciální techniky, kterými se lze chránit, probereme někdy v budoucnu.

Zdvojení všech znaků slova v řetězci

Problém vyřešíme tak, že vyhledáme všechny (přepínač g) výskyty znaků slova a ty jednoduše zdvojíme. Nejlepší řešení vede přes pamatování, ale abychom vyzkoušeli také něco dalšího, použijeme proměnnou $&.

  $_ = "LINUX & PERL";
  s/\w/$& x 2/eg;
  print;

Validace HTML tagu

HTML tagem může být <html>, <a href="http://www.linuxsoft.cz">, </b>, <img src="logo.png"/> nebo i <center >. Možností je celá řada. Pokusíme se je v co největší míře obsáhnout v našem řešení.

Tag bude vždy začínat znakem < a končit znakem >. Za úvodním < následuje případné lomítko a dále samotné jméno tagu. To je složeno z nenulového počtu znaků slova. Potom je možných ještě několik bílých znaků, dále opět nepovinné lomítko a nakonec znak >. To je struktura tagu, který nemá žádné parametry.

Argumenty se píší za jméno tagu. Může jich být libovolné množství. Každý z parametrů začíná bílým znakem. Tu následuje nenulový počet znaků slova. Nyní jsou v našem výrazu implementovány i přepínače. Ale stále chybí pravé parametry. Za přepínačem může nepovinně být rovnítko (případně obalené bílými znaky) a nějaká hodnota - v uvozovkách, apostrofech, nebo jako holé slovo.

U HTML tagů nezáleží na velikosti písmen. O to se ale starat nemusíme, protože jsme nikde konkrétní velikost neuváděli.

Když všechny tyto poznatky aplikujeme na regulární výraz, vznikne nám toto:

/
    <                       #začátek tagu
    \/?                     #případné lomítko
    \w+                     #jméno tagu
    (                       #parametry
        \s+                 #mezera
        \w+                 #parametr
        (                   #případná hodnota parametru
            \s*
            =               #rovnítko
            \s*
            ("[^"]*")       #v uvozovkách
            |               #nebo
            ('[^\']*')      #v apostrofech
            |               #nebo
            ([^\W]*)        #holé slovo
        )?
    )*
    \s*
    \/?                     #případné lomítko
    >                       #konec tagu
/x

Ale nemůže to stále být konečné řešení, protože vyhoví i řetězec </x/>, který tagem rozhodně není. Lomítko tedy může být maximalně jedno - buď na začátku nebo na konci. Toho docílíme rozvětvením.

/
    <                                                             #začátek tagu
    ((\/\w+(\s+\w+(\s*=\s*("[^"]*")|('[^\']*')|([^\W]*))?)*\s*)   #lomítko na začátku
    |                                                             #nebo
    (\w+(\s+\w+(\s*=\s*("[^"]*")|('[^\']*')|([^\W]*))?)*\s*\/?))  #případné lomítko na konci
    >                                                             #konec tagu
/x

Generování příkazů INSERT z dat textového souboru

Nakonec si uvedeme (po testování vstupu) asi nejpraktičtější příklad. Řekněme si, že máme data v nějakém textovém souboru a chceme je dostat do databáze. Ve zdrojovém souboru je máme k dispozici ve formátu sloupec\tsloupec\tsloupec... (sloupce oddělené tabulátorem), tedy například:

20050101	50000
20050102	84000
20050103	0
20050102	-20000
20050104	47000

Právě pro případ dvou sloupců si napíšeme skript, který data převede na INSERT příkazy:

INSERT INTO finance (datum, zisk) VALUES ("20050101", "50000");
INSERT INTO finance (datum, zisk) VALUES ("20050102", "84000");
INSERT INTO finance (datum, zisk) VALUES ("20050103", "0");
INSERT INTO finance (datum, zisk) VALUES ("20050104", "-20000");
INSERT INTO finance (datum, zisk) VALUES ("20050105", "47000");

Náš příklad je velmi konkrétní. Mělo by to usnadnit pochopení.

Postupně načteme každý řádek, rozdělíme na jednotlivé sloupce a vygenerujeme INSERT příkaz.

  open DATA, "data" or die "Nelze cist zdroj dat\n";
  open W, ">insert.sql" or die "Nelze zapisovat\n";
  
  while (<DATA>){
      ($datum, $zisk) = $_ =~ /(.+)\t(.+)/;
      chomp $zisk;
      print W "INSERT INTO finance (datum, zisk) VALUES (\"$datum\", \"$zisk\");\n";
  }
  
  close DATA;
  close W;

Poznámka - samozřejmě by šlo rozdělení řešit jednodušeji přes split:

  ($datum, $zisk) = split "\t", $_;

Zkusíme si ještě podobnou věc a to generování INSERT příkazů z dokumentů tabulkových editorů. Přesně toto jsem kdysi potřeboval a někde jsem našel velice hezké řešení. Prvním krokem je uložit dokument v nějakém textovém formátu - například CSV. CSV soubor může vypadat třeba takto:

0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;0;0;0;0;0;0;0;0;0;0
0;0;0;0;0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;0;0;0;0;0
0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;0;0;0;0
0;0;0;0;0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;0;0;0;0
0;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;0;0;0;0

Středníky jsou oddělovače sloupců a konce řádků oddělovače řádků. V každé buňce máme hodnotu 0 nebo 1. Tuto dvojrozměrnou strukturu budeme ukládat do jednoho rozměru. Pro každou buňku budeme tedy chtít v databázi uložit její souřadnice.

  my $radek;
  my @radek;
  my $cislo_radku = 0;
  my $jmeno_tabulky = "table";
  
  open DATA, "data.csv" or die "Nelze číst zdroj dat\n";
  open(W, ">insert.sql") or die "Nelze zapisovat\n";
  
  postupně načítáme jednotlivé řádky
  while ($radek = <DATA>){
      $cislo_radku++;
      @radek = $radek =~ /(?:([^;]*);)/g;#řádek (řetězec) je rozdělen na jednotlivé buňky

      #pro každou buňku aktuálního řádku vytvoříme INSERT příkaz
      for (my $cislo_sloupce=0; $cislo_sloupce<@radek; $cislo_sloupce++){
          chomp $radek[$cislo_sloupce];#je-li hodnota poslední v řádku, obsahuje i znak konce řádku a ten je třeba odstranit
          print W "INSERT INTO $jmeno_tabulky (x, y, hodnota) VALUES ($cislo_radku, $cislo_sloupce, $radek[$cislo_sloupce]);\n";
      }
  }
  close DATA;
  close W;

Poznámka - rozdělení by opět šlo řešit jednodušeji použitím funkce split:

      @radek = split(";", $radek);

Dnes jsem se pokusil ukázat široké využití regulárních výrazů. Doufám, že jsem vám dodal alespoň trochu inspirace. Příští díl bude ze série o regulárních výrazech poslední. Podíváme se na měření rychlosti a debugging.

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ů

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ář

11.2.2018 20:35 /Redakce Linuxsoft.cz
Třetí ročník odborné IT konference na téma Cloud computing v praxi proběhne ve čtvrtek 1. března 2018 v konferenčním centru Vavruška, v paláci Charitas, Karlovo náměstí 5, Praha 2 (u metra Karlovo náměstí) od 9:00 hod. dopoledne do cca 16 hod. odpoledne. Konference o trendech v oblasti cloud computingu nabídne i informace o konkrétních možnostech využívání cloudů a řešení vybraných otázek souvisejících s provozem IT infrastruktury.
Přidat komentář

15.1.2018 0:51 /František Kučera
První letošní pražský sraz se koná již tento čtvrtek 18. ledna od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Vítáni jsou všichni příznivci svobodného softwaru a hardwaru, ESP32, DIY, CNC, SDR nebo dobrého piva. Prvních deset účastníků srazu obdrží samolepku There Is No Cloud… just other people's computers. od Free Software Foundation.
Přidat komentář

14.11.2017 16:56 /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 – tradičně první čtvrtek před třetím pátkem v měsíci: 16. listopadu od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).
Přidat komentář

12.11.2017 11:06 /Redakce Linuxsoft.cz
PR: 4. ročník odborné IT konference na téma Datová centra pro business proběhne již ve čtvrtek 23. listopadu 2017 v konferenčním centru Vavruška, v paláci Charitas, Karlovo náměstí 5, Praha 2 (u metra Karlovo náměstí) od 9:00. Konference o návrhu, budování, správě a efektivním využívání datových center nabídne odpovědi na aktuální a často řešené otázky, např Jaké jsou aktuální trendy v oblasti datových center a jak je využít pro vlastní prospěch? Jak zajistit pro firmu či jinou organizaci odpovídající služby datových center? Podle jakých kritérií vybrat dodavatele služeb? Jak volit součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně spravovat datové centrum? Jak eliminovat možná rizika? apod.
Přidat komentář

13.9.2017 8:00 /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 – tentokrát netradičně v pondělí: 18. září od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).
Přidat komentář

3.9.2017 20:45 /Redakce Linuxsoft.cz
PR: Dne 21. září 2017 proběhne v Praze konference "Mobilní řešení pro business". Hlavní tématy konference budou: nejnovější trendy v oblasti mobilních řešení pro firmy, efektivní využití mobilních zařízení, bezpečnostní rizika a řešení pro jejich omezení, správa mobilních zařízení ve firmách a další.
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