Regulární výrazy v C++, Javascriptu a PHP

Regulární výraz je perfektní pomocník při práci s textem. Používá jej celá řada programů v Unixu. Regulární výraz umožní prohledávat soubory (grep, egrep), editovat je (sed, vi), analyzovat a vypočítávát zajímavé údaje (awk). Článek popisuje, jak regulární výrazy použít v jazycích C, C++, Javascript a PHP

Nedávno jsem potřeboval regulární výraz použít v C++. Původně jsem myslel, že bude třeba použít externí knihovnu, ale to jsem si nemohl dovolit. Nakonec jsem po prozkoumání knihoven pcre, boost narazil na posixový regex.h, který plně vyhovoval mým potřebám.

Regulární výraz je speciální řetězec znaků, který představuje určitý vzor pro textové řetězce. Regulární výrazy se proto nejčastěji používají ke kontrole dat zadávaných ve formulářích (například e-mailová adresa či PSČ) nebo „parsování” kódu (třeba HMTL, XML či CSV).

Regulární výrazy můžete v současné době najít téměř ve všech programovacích jazycích (Python, Javascript, Python, Php atd.). Znalost regulárních výrazů vám při programování může ušetřit mnoho řádků kódu (především různých „if-then-else” konstrukcí).

Existují různé odnože regulárních výrazů – nejznámější dvě odnože jsou Perl-compatible regulání výrazy a POSIX regulární výrazy. Ačkoliv většina novějších programovacích jazyků používá regulární výrazy odvozené od jazyka Perl, drobné rozdíly v implementaci tu stále přetrvávají.

Ukázka použití regulárního výrazu pomocí regexp.h

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <regex.h>

char enter_reverse_mode[] = "\33[7m";
char exit_reverse_mode[] = "\33[0m";

int main(int argc, char **argv)
{
  const char *pattern;
  int ec;
  regex_t expr;
  regmatch_t rm;
  char buf[12];
  size_t offset, length;
  int flags;
  assert(argc == 2);
  pattern = argv[1];
  if ((ec = regcomp(&expr, pattern, 0)) != 0) {
    char str[256];
    regerror(ec, &expr, str, sizeof str);
    fprintf(stderr, "%s: %s\n", pattern, str);
    return EXIT_FAILURE;
  }
  flags = 0;
  while (fgets(buf, sizeof buf, stdin)) {
    /* Find the end of the buffer */
    length = strcspn(buf, "\n");
    /* Check for beginning and end of line. */
    if (flags & REG_NOTEOL) {
            /* If the last line read was a partial line, then we are
             * not at the beginning of the line. */
      flags |= REG_NOTBOL;
      if (buf[length] == '\n')
        flags &= ~REG_NOTEOL;
    }
    else if (buf[length] != '\n') {
      /* We've read a partial line. */
      flags = REG_NOTEOL;
    }
    else {
      /* We have a complete line. */
      flags = 0;
    }
    /* get rid of any newline character */
    buf[length] = '\0';
    /* start at beginning of the buffer */
    offset = 0;
    while (regexec(&expr, buf + offset, 1, &rm, flags) == 0) {
      assert(rm.rm_so >= 0);
      /* we're not smart enough to support empty matches. */
      assert(rm.rm_eo > rm.rm_so);
      /* print the portion which precedes the match, then the match */
      printf("%.*s%s%.*s%s",
        rm.rm_so, buf + offset,
        enter_reverse_mode,
        rm.rm_eo - rm.rm_so, buf + offset + rm.rm_so,
        exit_reverse_mode);
      /* start next match at the end of this one. */
      offset += rm.rm_eo;
      /* we're no longer at the beginning of the line */
      flags |= REG_NOTBOL;
    }
    /* print remainder of the line */
    printf("%s", buf + offset);
    /* print a newline if we're at the end of a line */
    if (!(flags & REG_NOTEOL))
      putchar('\n');
  }
  return EXIT_SUCCESS;
}

Ukázka použití regulárního výrazu pomocí knihovny pcre

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <pcre.h>

char enter_reverse_mode[] = "\33[7m";
char exit_reverse_mode[] = "\33[0m";

int main(int argc, char **argv)
{
  const char *pattern;
  const char *errstr;
  int erroffset;
  pcre *expr;
  char line[512];
  assert(argc == 2); /* XXX fixme */
  pattern = argv[1];
  if (!(expr = pcre_compile(pattern, 0, &errstr, &erroffset, 0))) {
    fprintf(stderr, "%s: %s\n", pattern, errstr);
    return EXIT_FAILURE;
  }
  while (fgets(line, sizeof line, stdin)) {
    size_t len = strcspn(line, "\n");
    int matches[2];
    int offset = 0;
    int flags = 0;
    line[len] = '\0';
    while (0 < pcre_exec(expr, 0, line, len, offset, flags, matches, 2)) {
      printf("%.*s%s%.*s%s",
        matches[0] - offset, line + offset,
        enter_reverse_mode,
        matches[1] - matches[0], line + matches[0],
        exit_reverse_mode);
      offset = matches[1];
      flags |= PCRE_NOTBOL;
    }
    printf("%s\n", line + offset);
  }
  return EXIT_SUCCESS;
}

Regulární výrazy v PHP

V jazyce PHP existuje funkce Ereg, která zjistí, zda řetězec vyhovuje regulárnímu výrazu. Její předpis je následující

integer EReg(string regulární výraz, string řetězec, array shody)

Tato funkce vrací true, pokud řetězec vyhovuje regulárnímu výrazu, jinak vrací false. Pokud použijeme parametr shody, uloží se do pole řetězce, které vyhovují jednotlivým podvýrazům regulárního výrazu (ty jsou ohraničeny kulatými závorkami). Do prvku pole shody s indexem 0 se uloží ta část řetězce, která vyhovuje celému regulárnímu výrazu. Příklad v PHP mluví za vše

$text = "Nazdar ty programátore";
$a = Ereg("(Nazdar) (ty) (programátore)", $text, $x);

echo $x[0];//vypíše "Nazdar ty programátore"
echo $x[1];//vypíše "Nazdar"
echo $x[2];//vypíše "ty"
echo $x[3];//vypíše "Nazdar"

Další užitečnou funkcí je Ereg_Replace - ta nahradí řetězec podle regulárního výrazu. Její předpis je následující

string EReg_Replace(string regulární výraz, string náhrada, string řetězec)

Tato funkce nahradí část řetězce, která vyhovuje regulárnímu výrazu řetězcem náhrada. V řetězci náhrada lze použít i metaznak '\\číslice', který určuje podvýraz regulárního výrazu. Metaznak '\\0' určuje celý řetězec vyhovující regulárnímu výrazu. Příklad na nahrazení pomocí regulárního výrazu v PHP mluví za vše:

$text = "Nazdar ty programátore";
$x = Ereg_Replace("(Nazdar) (ty) (programátore)", "Ahoj", $text);
echo $x;//vypíše "Ahoj"

$y = Ereg_Replace("(Nazdar) (ty) (programátore)", "\\2 \\3 \\1", $text);
echo $y;//vypíše "ty programátore Nazdar"

$z = Ereg_Replace("(Nazdar) (ty) (programátore)", "\\0", $text);
echo $z;//vypíše "Nazdar ty programátore"

Regulární výrazy v Javascriptu

V javascript test pomocí regulárního výrazu zapisujeme ve tvaru regexp.test(řetězec), kde regexp je regulární výraz, kterému má odpovídat řezětec. Funkce vrací hodnotu true, pokud v řetězci byla nalezena shoda s regexpem, jinak vrací false. Příklad v Javascriptu:

var str="15 USD, 10 EUR, 300 CZK";
var re1=/(\d+)\s(\w+)/;
var result1=re1.test(str); //vrací: true
alert("Výsledek: "+result1);

V tomto příkladu není použit modifikátor g a tak se po nalezení shody (15 USD) v dalším hledání možných shod nepokračuje a funkce vrátí true.

Regulární výrazy v Pythonu

Python pro práci s regulárními výrazy nezavádí žádnou novou syntaxi, vše řeší pomocí modulů. Tento modul se jmenuje re a obsahuje několik funkcí a konstant, které jsou užitečné pro práci s regulárními výrazy.

Modul re používá regulární výrazy ve stylu jazyka Perl. Pro více informací si přečtěte Regular Expresion HOWTO, které spolu s dalšími HOWTO jazyka Python najdete na http://www.python.org/doc/howto.

Nejdůležitější funkcí je funkce re.match(), která má dva argumenty - regulární výraz a řetězec. Odpovídají-li znaky na začátku řetězce regulárnímu výrazu, vrátí objekt, který umožňuje získat další informace o výsledku, jestliže si neodpovídají, vrátí None. Podobnou funkcí, která ovšem prohledává celý řetězec, je re.search().

>>> import re

>>> print re.match('a[0-9]+', 'abcd')
None
>>> print re.match('a[0-9]+', 'a01234')
<_sre.SRE_Match object at 0x83626a0>
>>> print re.match('a[0-9]+', 'a0123b')
>_sre.SRE_Match object at 0x83633b8<

>>> m = re.match('a[0-9]+', 'a0123b')
>>> m.start()
0
>>> m.end()
5

Chceme-li používat jeden regulární výraz na více řetězců, je efektivnější ho nejprve "zkompilovat". Tuto práci za nás odvede funkce re.compile(), které regulární výraz předáme. Funkce provede jeho analýzu a vrátí objekt, jenž provádí efektivní vyhledávání zadaného vzorku. Tento objekt podporuje metody match(), search(), sub(), findall() a další.

Vyšlo 03.07.2008, v blogu: 0 1 2 3 4 5 6 7 8

Děkuji, že jste se rozhodl(a) přečíst tento článek. Budu rád i za komentář. Pokud Vás tento článek zaujal a rádi byste jej doporučili ostatním, podpořte mně prosím tím, že věnujete minutku svého času a uděláte mi reklamu na linkuj.cz, vybrali.sme.sk či jagg.cz. Přeji příjemné čtení

Poslední články

Diskuse k blogu

perlovske regularni vyrazy 
Clanok som prebehol iba velmi rychlo, pretoze POSIX regularne vyrazy su oproti tym perlovskym velmi slabe. Perlovske regexp maju take krasotinky ako vseliake flagy vyhladavania (multiline, extened, case sensitivity a co ja viem este ake). a napr namiesto dlhych [[:digit:]], [[:alnum:]], [[:space:]] a dalsich masakrov staci napisat d w s b.

Takze, kto xe robit s regularnymi vyrazmi nieco fakt vazne, fakt vrele odporucam perlovske a NIE POSIX regexpy! Nastudujte si ich a uvidite, ze regularne vyrazy vo vseobecnosti a hlavne perlovske vam zmenia zivot k lepsiemu :). Rucim za to :-P (PS: v dokumentacii k PHP je podla mna vyborny popis perlovskych regexp; PPS: JavaScript vyuziva tiez perlovske regexp!) 
Vložil: Peter (04.07.2008 08:36:31)
posix 
Hm hm hm, až jednou napíšete regexep, a on se bude matchovat a matchovat a matchovat ... a nikdy to neskončí, tak si možná pak přečtete něco o tom, že složitost matchování posixových regulárních výrazů nemůže -- na rozdíl od perlovych -- růst exponenciálně. Takže *identický* výraz preg matchne rychleji ale jinak tupý (a delší) posixový regexp snadno může matchnout rychlejí než krátký a chytrý pcre řešící tentýž problém, protože složitost je skrytá. Takže s rychlostí bych byl opatrný... 
Vložil: Dan (04.07.2008 08:39:10)
Přidání příspěvku
©PC-guru.cz 2000-2008 | Optimalizováno pro 1024*768