Narzędzia użytkownika

Narzędzia witryny


regex

Różnice

Różnice między wybraną wersją a wersją aktualną.

Odnośnik do tego porównania

Next revision
Previous revision
regex [2008/04/15 02:25]
przemoc utworzono szkic opisu boost::regex
regex [2008/04/17 03:03] (aktualna)
przemoc wersja finalna
Linia 1: Linia 1:
 ====== Wyrażenia regularne oczami programisty C++, czyli Boost.Regex ====== ====== Wyrażenia regularne oczami programisty C++, czyli Boost.Regex ======
  
-__Wersja robocza__+__Przemysław Pawełczyk G1ISI__
  
 W życiu każdego programisty przychodzi czas, w którym musi poznać wyrażenia regularne (z różnych przyczyn), a znając już je, używa ich chętnie i bez oporów, przynajmniej w skryptach shellowych (a de facto w programach typu [[wp>​AWK]],​ [[wp>​grep]] czy [[wp>​sed]]) i w interpretowanych językach jak [[wp>​PHP]],​ [[wp>​Perl]],​ [[wp>​Ruby_(programming_language)|Ruby]] oraz innych. W życiu każdego programisty przychodzi czas, w którym musi poznać wyrażenia regularne (z różnych przyczyn), a znając już je, używa ich chętnie i bez oporów, przynajmniej w skryptach shellowych (a de facto w programach typu [[wp>​AWK]],​ [[wp>​grep]] czy [[wp>​sed]]) i w interpretowanych językach jak [[wp>​PHP]],​ [[wp>​Perl]],​ [[wp>​Ruby_(programming_language)|Ruby]] oraz innych.
  
-Ogromne możliwości przetwarzania tekstu, jakie dają nam potocznie nazywane regexpy, ​ma się w końcu ochotę wykorzystać (jeżeli ma to sens, nic na siłę...) w tworzonych programach, właśnie npprzy użyciu języka ​C++. W czym problem? W obowiązującym aktualnie ​standardzie C++98 nie uwzględniono wyrażeń regularnychwięc możemy zapomnieć o wygodzie definiowania i posługiwaniu się nimi znanej z perla czy rubiego, ale to tak naprawdę jedyny problem. Zbliżający się kolejny standard ​C++0x naprawia ten błąd poprzez ​ączenie ​doń biblioteki ​Boost.Regex, ​która dostępna ​jest już dziśNie jest to jedyna biblioteka implementująca wyrażenia regularne w języku C++, ale wszystkie znaki na niebie i Ziemii wskazują, że będzie zapewne (jeżeli jeszcze nie jest) najbardziej rozpowszechnioną i najlepiej znaną biblioteką C++ tego typu w niedalekiej przyszłości. Tym razem [[wp>​PCRE]],​ [[http://​doc.trolltech.com/​latest/​qregexp.html|QRegExp]] (część [[wp>​Qt_(toolkit)|Qt]]) i inne rozwiązania zostaną ​więc przemilczane, ​ale kiedyś na pewno jeszcze o nich wspomnę...+Ogromne możliwości przetwarzania tekstu, jakie dają nam potocznie nazywane regexpy, ​można też wykorzystać w językach, ​których nie wbudowano obsługi wyrażeń regularnych,​ czyli np. w języku C++. Nie mówię tu o własnoręcznej implementacji np. niedeterministycznego automatu skończonego Thompsona, choć to też byłoby pewne wyjścieProgramiściz natury jednostki dość leniwe, często lubią korzystać z gotowych komponentów do budowy ​asnego oprogramowaniaNiestety, brak obsługi wyrażeń regularnych w standardowej bibliotece ​C++ standardzie C++98 (ISO/IEC 14882:1998) czy ostatnim C++03 (ISO/IEC 14882:​2003). Sytuację poprawia dopiero [[http://​www.open-std.org/​jtc1/​sc22/​wg21/​docs/​papers/​2005/​n1836.pdf|TR1]]który będzie częścią standardu ​C++0x, a którego ostateczne ujęcie i ratyfikacja nastąpią w najbliższych latach, optymistycznie:​ już w przyszłym roku. 
 + 
 +Co więc trafiło ​do przestrzeni ''​std::​tr1''?​ To, co dziś znamy jako biblioteka [[http://​www.boost.org/​doc/​libs/​1_35_0/​libs/​regex/​index.html|Boost.Regex]]której autorem ​jest [[http://​ourworld.compuserve.com/​homepages/​John_Maddock/​|John Maddock]]. 
 + 
 +Oczywiście nie jest to jedyna biblioteka implementująca wyrażenia regularne w języku C++, ale tym razem [[wp>​PCRE]],​ [[http://​doc.trolltech.com/​latest/​qregexp.html|QRegExp]] (część [[wp>​Qt_(toolkit)|Qt]]) i inne rozwiązania zostaną ​świadomie ​przemilczane, ​choć kiedyś na pewno jeszcze o nich wspomnę... 
 + 
 +===== Mikropokaz, czyli zapis imperatywny kontra deklaratywny ===== 
 + 
 +Mamy do zrealizowania mały program, który na wejściu przyjmuje maile, a na wyjściu podaje ich tematy, przy czym ewentualny przedrostek ''​Re:''​ nie należy do wyświetlanego tematu. 
 + 
 +Tak mogłoby wyglądać rozwiązanie imperatywne:​ 
 +<code cpp> 
 +std::string line; 
 +while (std::​getline(std::​cin,​ line)) 
 +
 +    if (line.compare(0,​ 9, "​Subject:​ ") == 0) 
 +    { 
 +        std::size_t offset = 9; 
 +        if (line.compare(offset,​ 4, "Re: ")) 
 +            offset += 4; 
 +        std::cout << line.substr(offset);​ 
 +    } 
 +
 +</​code>​ 
 + 
 +Korzystając z wyrażeń regularnych,​ w naszym przypadku poprzez Boost.Regex,​ "​wyłuskiwanie"​ tematu jesteśmy w stanie zrealizować deklaratywnie (pomijając istnienie niezbędnej pętli), a więc opisując to, czego szukamy, a nie jak: 
 +<code cpp> 
 +std::string line; 
 + 
 +// Asercja ^ jest zbędna gdy wyrażenie jest używane w funkcji boost::​regex_match,​ 
 +// ale jej obecność pozwala także na ewentualne zastosowanie funkcji 
 +// boost::​regex_search. Wyjaśnienie pojawi się w dalszej części... 
 +boost::​regex pat("​^Subject:​ (Re: )?​(.*)"​);​ 
 +boost::​smatch what; 
 + 
 +while (std::​getline(std::​cin,​ line)) 
 +
 +    if (boost::​regex_match(line,​ what, pat)) 
 +        std::cout << what[2]; 
 +
 +</​code>​ 
 + 
 +**Rozwiązanie deklaratywne** jest lepsze, ponieważ:​ 
 +  * opisuje to co chcemy osiągnąć (a nie przy użyciu jakiego algorytmu),​ 
 +  * jest bardziej zwięzłe,​ 
 +przez co jest też **łatwiejsze w utrzymaniu**. 
 + 
 +Wyobraźmy sobie teraz, że mamy maile wygenerowane przez program Outlook Express, który lubi dodawać więcej przedrostków ''​Re:'',​ aniżeli jest to potrzebne w dłuższych konwersacjach. Dodatkowo weźmy pod uwagę, że niektóre OE mogły być w polskiej wersji językowej i dodawały ''​Odp:''​ zamiast ''​Re:''​. \\ 
 +Poprawione wyrażenie regularne będzie wówczas wyglądać np. tak "''​^Subject:​ (Re: |Odp: )*(.*)''"​ i jest to jedyna zmiana niezbędna w rozwiązaniu deklaratywnym. Czytelnik zapewne z wielkim zapałem samodzielnie usprawni rozwiązanie imperatywne...
  
 ===== Przypomnienie składni wyrażeń regularnych ===== ===== Przypomnienie składni wyrażeń regularnych =====
  
-===== Przykłady ycia =====+Do wyboru mamy trzy główne składnie wyrażeń regularnych:​ 
 +  * Perl (domyślna),​ 
 +  * POSIX rozszerzona (z odmianami używanymi w ''​egrep''​ czy ''​awk''​),​ 
 +  * POSIX podstawowa (z odmianami ​ywanymi w ''​grep''​ czy ''​emacs''​).
  
 +Omówię pokrótce tylko najważniejsze elementy domyślnej składni.
 +
 +==== Perlowa składnia wyrażeń regularnych ====
 +
 +            Zapis          ^                 ​Znaczenie
 +  .                        | dowolny znak
 +  ^                        | początek linii
 +  $                        | koniec linii
 +  (  )                     | początek i koniec znaczonego grupowania
 +  (?:   ​) ​                 | początek i koniec nieznaczonego grupowania
 +  *                        | powtórzenie atomu zero lub więcej razy
 +  +                        | powtórzenie atomu jeden lub więcej razy
 +  ?                        | powtórzenie atomu zero lub jeden raz
 +  {n}                      | powtórzenie atomu dokładnie n razy
 +  {n,}                     | powtórzenie atomu n lub więcej razy
 +  {n,m}                    | powtórzenie atomu od n do m razy
 +  |                        | alternatywa
 +  [znaki^odrzuty] ​         | dopuszczalny znaki i niedopuszczalne odrzuty
 +  \<  \>   ​\b ​             | początek, koniec słowa i którekolwiek z nich
 +  \d  <​=> ​ [[:​digit:​]] ​    | cyfra
 +  \l  <​=> ​ [[:​lower:​]] ​    | mała litera
 +  \s  <​=> ​ [[:​space:​]] ​    | spacja
 +  \u  <​=> ​ [[:​upper:​]] ​    | duża litera
 +  \w  <​=> ​ [[:​word:​]] ​     | znak "​słowny"​ (alfanumeryczny lub _)
 +  \D  <​=> ​ [^[:​digit:​]] ​   | nie: cyfra
 +  \L  <​=> ​ [^[:​lower:​]] ​   | nie: mała litera
 +  \S  <​=> ​ [^[:​space:​]] ​   | nie: spacja
 +  \U  <​=> ​ [^[:​upper:​]] ​   | nie: duża litera
 +  \W  <​=> ​ [^[:​word:​]] ​    | nie :znak "​słowny"​
 +  (?#​komentarz) ​           | ignorowany (komentarz)
 +  (?​=wzorzec) ​             | "​zjada"​ 0 znaków tylko jeżeli wzorzec się zgadza
 +  (?​!wzorzec) ​             | "​zjada"​ 0 znaków tylko jeżeli wzorzec nie pasuje
 +
 +Wszystkie operatory powtórzenia są domyślnie zachłanne (ang. //​greedy//​),​ czyli starają się pokryć jak największy obszar tekstu. Odwrócenie tego zachowania możemy uzyskać poprzez dodanie ''?''​ na końcu takiego operatora, uzyskując np. ''​*?''​ lub ''​{n,​}?''​.
 +
 +Na koniec nie sposób nie wspomnieć o odwołaniach wstecznych (ang. //​backreferences//​),​ które stosujemy poprzez poprzedzenie znakiem backslash (\) numeru grupy znaczonej, dzięki którym możemy jeszcze bardziej złożone wyrażenia regularne budować. Wyrażenie regularne odpowiedzialne za wyszukiwanie powtórzeń w tekście mogłoby wyglądać tak:
 +  (\w) \1
 +
 +===== Zaczynamy zabawę =====
 +
 +==== Niezbędnik ====
 +
 +Do skorzystania z dobrodziejstw Boost.Regex niezbędne jest dołączenie pliku nagłówkowego:​
 +<code cpp>
 +#include <​boost/​regex.hpp>​
 +</​code>​
 +Przy linkowaniu musimy natomiast dołączyć bibliotekę Boost.Regex,​ co w systemach *nixowych odbywa się za pomocą parametru ''​-lboost_regex''​.
 +
 +Tak jak w przypadku innych bibliotek Boosta, wszystkie stałe, klasy i funkcje zewnętrzne znajdują się przestrzeni nazw ''​boost''​ i dla przejrzystości nie będę tego pisał przy przytaczaniu fragmentów definicji podstawowych elementów Boost.Regex.
 +
 +==== Wyrażenie regularne ====
 +
 +Wyrażeniu regularnemu odpowiada szablon klasy ''​boost::​basic_regex'',​ który parametryzowany jest typem znaku i klasą cech tego typu (która domyślnie jest definiowana poprzez szablon ''​regex_traits''​ parametryzowany wcześniej podanym typem znaku). ​
 +<code cpp>
 +template < class charT,
 +           class traits = regex_traits<​charT>​ >
 +class basic_regex { /*...*/ };
 +</​code>​
 +
 +Dla wygody zdefiniowane są nasŧepujące typy:
 +<code cpp>
 +typedef basic_regex<​char> ​    ​regex;​
 +typedef basic_regex<​wchar_t> ​ wregex;
 +</​code>​
 +
 +Ważniejsze metody tego szablonu to:
 +<code cpp>
 +bool empty() const; ​         // zwraca true jeżeli obiekt nie zawiera żadnego poprawnego wyrażenia regularnego
 +unsigned mark_count() const; // zwraca liczbę znaczonych podwyrażeń w wyrażeniu regularnym
 +flag_type flags() const; ​    // zwraca maskę bitową zawierającą flagi trybu przetwarzania wyrażenia regularnego
 +</​code>​
 +
 +Najważniejszy jest jednak konstruktor:​
 +<code cpp>
 +explicit basic_regex(const charT* p, flag_type f = regex_constants::​normal);​
 +</​code>​
 +który przyjmuje sekwencję znaków zawierającą wyrażenie regularne oraz tryb przetwarzania tego wyrażenia.
 +W przypadku niepoprawnego wyrażenia regularnego rzucany jest wyjątek ''​boost::​regex_error''​ (dawniej ''​boost::​bad_expression''​ lub ''​boost::​bad_pattern'',​ które są teraz aliasami do ''​boost::​regex_error''​ dla wstecznej kompatybilności),​ chyba że przekazano flagę ''​regex_constants::​no_except''​.
 +
 +Mamy następujące podstawowe (nieuzależnione od wybranej składni) tryby przetwarzania:​
 +<code cpp>
 +namespace regex_constants {
 +    typedef implementation-specific-bitmask-type syntax_option_type;​
 +
 +    static const syntax_option_type normal; ​              // Domyślna składnia RE języka Perl
 +    static const syntax_option_type ECMAScript = normal; ​ // i jej aliasy...
 +    static const syntax_option_type JavaScript = normal;
 +    static const syntax_option_type JScript = normal;
 +    static const syntax_option_type perl = normal;
 +    static const syntax_option_type basic; ​               // Składnia POSIX podstawowa
 +    static const syntax_option_type sed = basic; ​         // i jej najpopularniejszy alias
 +    static const syntax_option_type grep;                 // oraz odmiana.
 +    static const syntax_option_type extended; ​            // Składnia POSIX rozszerzona
 +    static const syntax_option_type awk;                  // z dwiema odmianami.
 +    static const syntax_option_type egrep;
 +    static const syntax_option_type icase; ​               // Ignorowanie wielkości liter 
 +                                                          // w dopasowaniach
 +    static const syntax_option_type nosubs; ​              // Zaniechanie tworzenia podwyrażen
 +                                                          // w dopasowaniach
 +    static const syntax_option_type optimize; ​            // Optymalizacja poźniejszego dopasowywania
 +                                                          // nawet kosztem dłuższego tworzenia obiektu.
 +    static const syntax_option_type collate; ​             // Zakresy znaków przekazywane w nawiasach
 +                                                          // kwadratowych powinny uwzględniać lokalizm.
 +}
 +</​code>​
 +
 +Dla wygody powyższe stałe zdefiniowane są także w obrębie szablonu klasy ''​basic_regex''​.
 +
 +==== Dopasowania ====
 +
 +Oprócz klasy reprezentującej wyrażenie regularne potrzebujemy także klasy przechowującej znalezione dopasowania w zadanym tekście. Służy do tego szablon klasy ''​boost::​match_results''​ parametryzowany typem iteratora obsługującego sekwencję wejściową:​
 +<code cpp>
 +template < class BidirectionalIterator,​
 +           class Allocator = std::​allocator<​sub_match<​BidirectionalIterator>​ >
 +class match_results { /*...*/ };
 +</​code>​
 +
 +Podobnie jak wcześniej, zdefiniowano następujące typy dla wygodniejszego użycia:
 +<code cpp>
 +typedef match_results<​const char*> ​             cmatch;
 +typedef match_results<​const wchar_t*> ​          ​wcmatch;​
 +typedef match_results<​string::​const_iterator> ​  ​smatch;​
 +typedef match_results<​wstring::​const_iterator> ​ wsmatch;
 +</​code>​
 +
 +Dopasowania to tak naprawdę kolekcje poddopasowań ''​boost::​sub_match'',​ do których dostęp mamy jedynie poprzez ''​operator[]''​ klasy ''​boost::​match_results''​. Obiekt klasy ''​boost::​sub_match''​ posiada kilka przydatnych składowych:​ boolowską ''​matched''​ informującą o tym czy dane znaczone podwyrażenie brało udział w dopasowaniu i jeżeli tak, to iteratory ''​first'',​ ''​second''​ wskazujące na początek oraz koniec (z wyłączeniem) w ciągu, który pokrył to dopasowanie.
 +
 +Mając obiekt tej klasy, nazwijmy go m, odwołania do poszczególnych elementów wyglądają następująco:​
 +^ Boost.Regex ^ Perl ^ Opis ^
 +| m.prefix() ​ | $`   | treść poprzedzająca dopasowanie ​ |
 +| m[0]        | $& ​  | cała dopasowana treść ​           |
 +| m[1]        | $1   | pierwsze znaczone podwyrażenie ​  |
 +|         ​... ​      || kolejne znaczone podwyrażenia ​   |
 +| m[n]        | $n   | n-te znaczone podwyrażenie ​      |
 +| m.suffix() ​ | $' ​  | treść następująca po dopasowaniu |
 +
 +Szablon klasy ''​match_results''​ posiada także metody
 +<code cpp>
 +const_iterator begin() const; ​ // Zwraca startowy iterator znaczonych podwyrażeń.
 +const_iterator end() const; ​   // Zwraca terminujący iterator znaczonych podwyrażeń.
 +</​code>​
 +które ułatwiają realizację wygodnego iterowania po znaczonych podwyrażeniach.
 +
 +Dostępne są ponadto różne tryby dopasowań, przekazywane w funkcjach niżej omówionych. Przykłady to:
 +<code cpp>
 +namespace regex_constants {
 +    typedef implemenation-specific-bitmask-type match_flag_type;​
 +    ​
 +    static const match_flag_type match_default = 0;      // Zachowanie domyślne (zgodne z ECMA-262)
 +    /* ... */
 +    static const match_flag_type match_not_null; ​        // Dopasowanie nie może być puste.
 +    /* ... */
 +    static const match_flag_type match_single_line; ​     // ^ odpowiada za początek tekstu,
 +                                                         // a $ - koniec tekstu.
 +    /* ... */    ​
 +    static const match_flag_type format_no_copy; ​        // Sekcje tekstu niepasujące do wyrażenia
 +                                                         // regularnego nie są kopiowane do
 +                                                         // wyjściowego łańcucha znaków.
 +    /* ... */
 +    static const match_flag_type format_all; ​            // Aktywuje wszystkie dostępne rozszerzenia
 +                                                         // składni, włączając w to warunkowe podstawienia.
 +}
 +</​code>​
 ===== Możliwości Boost.Regex ===== ===== Możliwości Boost.Regex =====
  
-===== Konkurencja ​na własnym podwórkuczyli Boost.Xpressive =====+Same szablony klas ''​boost::​basic_regex''​ oraz ''​boost::​match_results''​ byłyby nic nie warte bez funkcji, które na nich operują realizując podstawowe przypadki użycia wyrażen regularnych.  
 + 
 +Poniżej omówione szablony funkcji parametryzowane są różnymi elementami w zależności od wersji (pominę dokładne deklaracje tych szablonów),​ ale zawsze jest to m.in. typ znaku. 
 + 
 +==== Dopasowywanie (matching) - regex_match ​==== 
 + 
 +<code cpp> 
 +// Wersja bez zapisywania odnalezionych dopasowań. 
 +template </* */> 
 +bool regex_match(const basic_string</​* */>& s, 
 +                 const basic_regex</​* */>& e, 
 +                 ​match_flag_type flags = match_default);​ 
 + 
 +// Wersja zapisująca odnalezione dopasowania w obiekcie klasy match_results. 
 +template </* */> 
 +bool regex_match(const basic_string</​* */>& s, 
 +                 ​match_results</​* */>& m, 
 +                 const basic_regex</​* */>& e, 
 +                 ​match_flag_type flags = match_default);​ 
 + 
 +// Wersja główna, wołana także przez powyższe 
 +template <class BidirectionalIterator,​ class Allocator, class charT, class traits>​ 
 +bool regex_match(BidirectionalIterator first, BidirectionalIterator last, 
 +                 ​match_results<​BidirectionalIterator,​ Allocator>&​ m, 
 +                 const basic_regex<​charT,​ traits>&​ e, 
 +                 ​match_flag_type flags = match_default);​ 
 +</​code>​ 
 + 
 +Chyba najważniejsze w tej funkcji jest to, że dopasowaniu musi ulec cały tekst, dlatego też asercje użyte ​na początku i końcu wyrażenia regularnego (^ i $ odpowiednio) nie mają najmniejszego znaczenia (o czym już wcześniej wspomniałem ​mikropokazie). 
 + 
 +Najczęściej używana jest do badania poprawności przekazanych danych. 
 + 
 +<code cpp> 
 +#include <​iostream>​ 
 +#include <​string>​ 
 +#include <​boost/​regex.hpp>​ 
 + 
 +namespace validations { 
 +    // Wyrażenie regularne zgodne z formatem IBAN 
 +    // IBAN = International Bank Account Number 
 +    static const boost::​regex ibanFormat("​[A-Z]{2}\\d{2} ?​[A-Z\\d]{4}(?:​ ?​\\d{4}){1,​} ?​\\d{1,​4}"​);​ 
 + 
 +    // Sprawdź czy string ma format IBAN 
 +    bool hasIbanFormat(const std::​string&​ s) 
 +    { 
 +        return regex_match(s,​ ibanFormat);​ 
 +    }     
 +
 + 
 +// Wczytaj linię z wejścia i sprawdź czy to może być IBAN, 
 +// wówczas zwróć 1, w przeciwnym przypadku - 0. 
 +int main() { 
 +    std::string line; 
 +    std::​getline(std::​cin,​ line); 
 +    std::cout << validations::​hasIbanFormat(line) << std::​endl;​ 
 +    return 0; 
 +
 +</​code>​ 
 + 
 +==== Przeszukiwanie (searching) - regex_search ==== 
 + 
 +<code cpp> 
 +// Wersja bez zapisywania odnalezionych dopasowań. 
 +template </* */> 
 +bool regex_search(const basic_string</​* */>& s, 
 +                 const basic_regex</​* */>& e, 
 +                 ​match_flag_type flags = match_default);​ 
 + 
 +// Wersja zapisująca odnalezione dopasowania w obiekcie klasy match_results. 
 +template </* */> 
 +bool regex_search(const basic_string</​* */>& s, 
 +                 ​match_results</​* */>& m, 
 +                 const basic_regex</​* */>& e, 
 +                 ​match_flag_type flags = match_default);​ 
 + 
 +// Wesja głównawołana także przez powyższe 
 +template <class BidirectionalIterator,​ class Allocator, class charT, class traits>​ 
 +bool regex_search(BidirectionalIterator first,  
 +                  BidirectionalIterator last, 
 +                  match_results<​BidirectionalIterator,​ Allocator>&​ m, 
 +                  const basic_regex<​charT,​ traits>&​ e, 
 +                  match_flag_type flags = match_default);​ 
 +</​code>​ 
 + 
 +Prosty przykład użycia korzystający również z iteratora po wszystkich dopasowaniach wyrażenia regularnego. 
 +<code cpp> 
 +#include <​iostream>​ 
 +#include <​string>​ 
 +#include <​boost/​regex.hpp>​ 
 + 
 +// Callback dla funkcji std::​for_each operujący na wynikach dopasowania. 
 +bool printRep(const boost::​smatch&​ what) 
 +
 +    std::cout << what[1] << " "; ​ // Wyświetl na standardowe wyjście pierwsze podwyrażenie. 
 +    return true;                  // Nie zatrzymuj wykonywania pętli for_each. 
 +
 + 
 +// Wczytaj linię z wejścia i sprawdź czy są w niej powtórzenia "​słów",​ 
 +// a jeżeli są, podaj jakie. 
 +int main() { 
 +    // Wyrażenie regularne reprezentujące powtarzające się słowa. 
 +    boost::​regex rep("​(\\<​\\w+\\>​)(?:​\\s+\\<​\\1\\>​)+"​);​ 
 +    std::string line; 
 +    std::​getline(std::​cin,​ line); 
 +    // Początkowy iterator dopasowań do wyrażen regularnych. 
 +    boost::​sregex_iterator begin(line.begin(),​ line.end(), rep); 
 +    // Terminalny iterator dopasowań do wyrażen regularnych. 
 +    boost::​sregex_iterator end; 
 +    // Dla każdego dopasowania wywołaj funkcję printRep. 
 +    std::​for_each(begin,​ end, &​printRep);​ 
 +    std::cout << std::​endl;​ 
 +    return 0; 
 +
 +</​code>​ 
 + 
 +==== Zastępowanie (replacing) - regex_replace ==== 
 + 
 +<code cpp> 
 +// Wersja wygodna 
 +template </* */> 
 +basic_string</​* */> regex_replace(const basic_string</​* */>& s, 
 +                                  const basic_regex</​* */>& e, 
 +                                  const basic_string</​* */>& fmt, 
 +                                  match_flag_type flags = match_default);​ 
 + 
 +// Wersja główna 
 +template <class OutputIterator,​ class BidirectionalIterator,​ class traits, class charT> 
 +OutputIterator regex_replace(OutputIterator out, 
 +                             ​BidirectionalIterator first, 
 +                             ​BidirectionalIterator last, 
 +                             const basic_regex<​charT,​ traits>&​ e, 
 +                             const basic_string<​charT>&​ fmt, 
 +                             ​match_flag_type flags = match_default);​ 
 +</​code>​ 
 + 
 +W domyślnej składni stringu formatującego (przy braku flagi ''​regex_constants:​literal''​) zdefiniowane są sekwencje specjalne odnoszące się do podmienianego dopasowania takie jak w Perlu (zaprezentowane zostały wcześniej w tabelce [[#​Dopasowania]]). Aby uzyskać na wyjściu znak dolara ($), trzeba napisać go dwukrotnie. 
 + 
 +Jako przykład zastosowania przedstawię kod aplikacji wyświetlającej na wyjście przekazany w 1. argumencie plik CSV (Comma-Separated Values) z zamienionymi miejscami pierwszymi dwiema kolumnami, a kolumny te są oddzielone przecinkiem. 
 +<code cpp> 
 +#include <​iostream>​ 
 +#include <​fstream>​ 
 +#include <​string>​ 
 +#include <​boost/​regex.hpp>​ 
 + 
 +// Załaduj strumień wejściowy do stringa. 
 +void loadFile(std::​string&​ s, std::​istream&​ is) 
 +
 +    s.erase();​ 
 +    s.reserve(is.rdbuf()->​in_avail());​ 
 +    char c; 
 +    while (is.get(c)) 
 +    { 
 +        if (s.capacity() == s.size()) 
 +            s.reserve(s.capacity() * 3); 
 +        s.append(1, c); 
 +    } 
 +
 + 
 +// Wyświetl plik CSV (o nazwie przekazanej w 1. argumencie) z zamienionymi pierwszymi 2 kolumnami 
 +int main(int argc, const char** argv) 
 +
 +    // Wyrażenie regularne reprezentujące pierwsze dwie kolumny (druga może być pusta) 
 +    boost::​regex re("​^([^,​\n]*),?​([^,​\n]*)"​);​ 
 +    // Format stringu odpowiadający podmienianemu dopasowaniu. 
 +    std::string fmt("​\\2,​\\1"​);​ 
 +    try { 
 +        std::​ifstream fs(argv[1]);​ 
 +        std::string in; 
 +        loadFile(in,​ fs); 
 +        // Wyświetl na wyjściu string in z zamienionymi dopasowaniami re na string formatujący fmt. 
 +        std::cout << boost::​regex_replace(in,​ re, fmt); 
 +    } 
 +    catch(...) { 
 +        return -1; 
 +    } 
 +    return 0; 
 +
 +</​code>​ 
 + 
 +==== Dzielenie (tokenizing) - regex_token_iterator ==== 
 + 
 +''​boost::​regex_token_iterator''​ to już nie funkcja a szablon klasy będącej adapterem iteratora. 
 + 
 +<code cpp> 
 +template <class BidirectionalIterator,​  
 +          class charT = iterator_traits<​BidirectionalIterator>::​value_type,​ 
 +          class traits = regex_traits<​charT>​ > 
 +class regex_token_iterator { 
 +    /* ... */ 
 +    regex_token_iterator(BidirectionalIterator a,  
 +                         ​BidirectionalIterator b,  
 +                         const basic_regex<​charT,​ traits>&​ re,  
 +                         int submatch = 0,  
 +                         ​match_flag_type m = match_default);​ 
 +   ​regex_token_iterator(BidirectionalIterator a,  
 +                        BidirectionalIterator b,  
 +                        const basic_regex<​charT,​ traits>&​ re,  
 +                        const std::​vector<​int>&​ submatches,  
 +                        match_flag_type m = match_default);​ 
 +    /* ... */ 
 +}; 
 +</​code>​ 
 + 
 +Po 2 przykłady użycia odsyłam na sam dół strony z dokumentacją [[http://​www.boost.org/​doc/​libs/​1_35_0/​libs/​regex/​doc/​html/​boost_regex/​ref/​regex_token_iterator.html|regex_token_iterator]]. 
 + 
 +===== Boost.Xpressive ​- relatywnie młody konkurent ===== 
 + 
 +Ważne innowacje w stosunku do Boost.Regex:​ 
 +  * obsługa tworzenia (obok dynamicznych,​ podobnych w zapisie do Boost.Regex) statycznych wyrażeń regularnych,​ podobnych w zapisie do Boost.Spirit,​ przez przeciążanie operatorów i zastosowanie techniki zwanej szablonami wyrażeniowymi (ang. //​expression templates//​) - owa statyczność daje wiele możliwości,​ m.in. kontrolę poprawności takiego wyrażenia już w czasie kompilacji,​ 
 +  * możliwość mieszania statycznych i dynamicznych wyrażeń regularnych,​ 
 +  * możliwość osadzania wyrażeń regularnych w sobie poprzez referencje. 
 + 
 +===== Kiedy co używać? ​=====
  
-===== Wydajność =====+|                                                          ^ Regex ^ Spirit ^ Xpressive ^ 
 +^ Dopasowywanie wzorców ad-hoc, języki regularne ​          ​| ​ tak  |  tak   ​| ​   tak    | 
 +^ Ustrukturalizowane parsowanie, gramatyki bezkontekstowe ​ |   ​- ​  ​| ​ tak   ​| ​   tak    | 
 +^ Manipulowanie tekstem ​                                   |  tak  |  tak   ​| ​   tak    | 
 +^ Akcje semantyczne,​ manipulowanie stanem programu ​        ​| ​  ​- ​  ​| ​ tak   ​| ​    ​- ​    | 
 +^ Dynamiczność - nowe deklaracje w trakcie działania ​      ​| ​ tak  |   ​- ​   |    tak    | 
 +^ Statyczność - brak nowych deklaracji w trakcie działania |  tak  |  tak   ​| ​   tak    | 
 +^ Wyczerpujące przeszukiwanie z nawrotami ​                 |  tak  |   ​- ​   |    tak    |
  
 +Małego komentarza może wymagać wyczerpujące przeszukiwanie z nawrotami (ang. //​exhaustive backtracking//​). Chodzi o to, że przymiotnik "​wyczerpujące"​ nie opisuje samego procesu przeszukiwania z nawrotami, które w istocie jest wyczerpujące,​ a konsekwencję w jego stosowaniu, która jest wymagana od wyrażeń regularnych. Boost.Spirit stosuje backtracking,​ jednak gdy znajduje jakieś częściowe dopasowanie,​ to nie "cofa się", żeby możliwe było zrealizowanie reszty reguły, tylko uznaje, że dopasowanie się nie powiodło. Dlatego poniższy przykład nigdy nie zadziała:
 +<code cpp>
 +*chlit_p('​a'​) >> chlit_p('​a'​);​
 +</​code>​
regex.1208219153.txt.gz · ostatnio zmienione: 2008/04/15 02:25 przez przemoc