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

Both sides previous revision Previous revision
Next revision
Previous revision
regex [2008/04/16 16:01]
przemoc wersja preprefinalna
regex [2008/04/17 03:03]
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 ======
  
-[[przemoc@gmail.com|Przemysław Pawełczyk ​G1ISI]]+__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.
Linia 80: Linia 80:
   |                        | alternatywa   |                        | alternatywa
   [znaki^odrzuty] ​         | dopuszczalny znaki i niedopuszczalne odrzuty   [znaki^odrzuty] ​         | dopuszczalny znaki i niedopuszczalne odrzuty
 +  \<  \>   ​\b ​             | początek, koniec słowa i którekolwiek z nich
   \d  <​=> ​ [[:​digit:​]] ​    | cyfra   \d  <​=> ​ [[:​digit:​]] ​    | cyfra
   \l  <​=> ​ [[:​lower:​]] ​    | mała litera   \l  <​=> ​ [[:​lower:​]] ​    | mała litera
Linia 91: Linia 92:
   \W  <​=> ​ [^[:​word:​]] ​    | nie :znak "​słowny"​   \W  <​=> ​ [^[:​word:​]] ​    | nie :znak "​słowny"​
   (?#​komentarz) ​           | ignorowany (komentarz)   (?#​komentarz) ​           | ignorowany (komentarz)
-  (?:wzorzec) ​             | "​zjada"​ 0 znaków tylko jeżeli wzorzec się zgadza+  (?=wzorzec) ​             | "​zjada"​ 0 znaków tylko jeżeli wzorzec się zgadza
   (?​!wzorzec) ​             | "​zjada"​ 0 znaków tylko jeżeli wzorzec nie pasuje   (?​!wzorzec) ​             | "​zjada"​ 0 znaków tylko jeżeli wzorzec nie pasuje
  
Linia 107: Linia 108:
 #include <​boost/​regex.hpp>​ #include <​boost/​regex.hpp>​
 </​code>​ </​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. 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.
  
Linia 133: Linia 136:
 Najważniejszy jest jednak konstruktor:​ Najważniejszy jest jednak konstruktor:​
 <code cpp> <code cpp>
-explicit basic_regex(const ​ charT* p, flag_type f = regex_constants::​normal);​+explicit basic_regex(const charT* p, flag_type f = regex_constants::​normal);​
 </​code>​ </​code>​
 który przyjmuje sekwencję znaków zawierającą wyrażenie regularne oraz tryb przetwarzania tego wyrażenia. 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''​. 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):+Mamy następujące podstawowe (nieuzależnione od wybranej składni) tryby przetwarzania:​
 <code cpp> <code cpp>
 namespace regex_constants { namespace regex_constants {
Linia 184: Linia 187:
 </​code>​ </​code>​
  
-Dopasowania to tak naprawdę kolekcje poddopasowań ''​boost::​sub_match'',​ do których dostęp mamy jedynie poprzez ''​operator[]''​ klasy ''​boost::​match_results''​.+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 =====
  
 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. ​ 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 zajemności od wersji (pominę dokładne deklaracje tych szablonów),​ ale zawsze jest to m.in. typ znaku.+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 ==== ==== Dopasowywanie (matching) - regex_match ====
Linia 214: Linia 253:
                  const basic_regex<​charT,​ traits>&​ e,                  const basic_regex<​charT,​ traits>&​ e,
                  ​match_flag_type flags = match_default);​                  ​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 w 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>​ </​code>​
  
Linia 232: Linia 302:
                  ​match_flag_type flags = match_default);​                  ​match_flag_type flags = match_default);​
  
-// Wesja główna, wołana także przez powyższze+// Wesja główna, wołana także przez powyższe
 template <class BidirectionalIterator,​ class Allocator, class charT, class traits> template <class BidirectionalIterator,​ class Allocator, class charT, class traits>
 bool regex_search(BidirectionalIterator first, ​ bool regex_search(BidirectionalIterator first, ​
Linia 239: Linia 309:
                   const basic_regex<​charT,​ traits>&​ e,                   const basic_regex<​charT,​ traits>&​ e,
                   match_flag_type flags = match_default);​                   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>​ </​code>​
  
Linia 259: Linia 360:
                              const basic_string<​charT>&​ fmt,                              const basic_string<​charT>&​ fmt,
                              ​match_flag_type flags = match_default);​                              ​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>​ </​code>​
  
Linia 282: Linia 427:
                         match_flag_type m = match_default);​                         match_flag_type m = match_default);​
     /* ... */     /* ... */
-}+};
 </​code>​ </​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 ===== ===== Boost.Xpressive - relatywnie młody konkurent =====
  
-Ważne innowacje:+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,   * 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ść mieszania statycznych i dynamicznych wyrażeń regularnych,​
regex.txt · ostatnio zmienione: 2008/04/17 03:03 przez przemoc