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 Both sides next revision
regex [2008/04/15 02:25]
przemoc utworzono szkic opisu boost::regex
regex [2008/04/16 02:37]
przemoc małe zbliżenie do finalnej wersji
Linia 5: Linia 5:
 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 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,​ a w naszym przypadku z Boost.Regex,​ "​wyłuskiwanie"​ tematu jesteśmy w stanie zrealizować deklaratywnie (pomijając istnienie niezbędnej pętli), a więc opisując 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 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 podstawowe (z odmianami uż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 
 +  \d  <​=> ​ [[:​digit:​]] ​    | cyfra 
 +  \l  <​=> ​ [[:​lower:​]] ​    | mała litera 
 +  \s  <​=> ​ [[:​space:​]] ​    | spacja 
 +  \ <​=> ​ [[:​upper:​]] ​    | duża litera 
 +  \w  <=>  [[:​word:​]] ​     | znak mogący być elementem wyrazu 
 +  \D  <=>  [^[:​digit:​]] ​   | nie cyfra 
 +  \L  <=>  [^[:​lower:​]] ​   | nie mała litera 
 +  \S  <=>  [^[:​space:​]] ​   | nie spacja 
 +  \U  <=>  [^[:​upper:​]] ​   | nie duża litera 
 +  \W  <​=> ​ [^[:​word:​]] ​    | nie znak mogący być elementem wyrazu 
 +  (?#​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, 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,​ 
 + 
 +===== Zaczynamy zabawę ===== 
 + 
 +Do skorzystania z dobrodziejstw Boost.Regex niezbędne jest dołączenie pliku nagłówkowego:​ 
 +<code cpp> 
 +#include <​boost/​regex.hpp>​ 
 +</​code>​ 
 + 
 +Wyrażeniu regularnemu odpowiada szablon klasy ''​boost::​basic_regex'',​ który parametryzowany jest m.in. typem znaku. Metody tego szablonu to: 
 +<code cpp> 
 +bool empty() const; ​         // zwraca true jeżeli w 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>​ 
 + 
 +Dla wygody, wewnątrz przestrzeni ''​boost''​ zdefiniowane są nasŧepujące typy: 
 +<code cpp> 
 +typedef basic_regex<​char>​ regex; 
 +typedef basic_regex<​wchar_t>​ wregex; 
 +</​code>​
  
 ===== Możliwości Boost.Regex ===== ===== Możliwości Boost.Regex =====
  
-===== Konkurencja na własnym podwórku, czyli Boost.Xpressive =====+==== Dopasowywanie (matching) - regex_match ==== 
 + 
 +==== Przeszukiwanie (searching) - regex_search ==== 
 + 
 +==== Zastępowanie (replacing) - regex_replace ==== 
 + 
 +==== Dzielenie (tokenizing) - regex_token_iterator ==== 
 + 
 +===== Boost.Xpressive ​- relatywnie młody konkurent ​===== 
 + 
 +Ważne innowacje:​ 
 +  * 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 poprzez referencje.
  
-===== Wydajność =====+===== Kiedy co używać=====
  
 +|                                                          ^ 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    |
regex.txt · ostatnio zmienione: 2008/04/17 03:03 przez przemoc