<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <atom:link href="https://jacek.zlydach.pl/blog/tags/regexp-feed.xml" rel="self" type="application/rss+xml"/>
    <title>Posts tagged: regexp - Jacek Złydach - blog</title>
    <link>https://jacek.zlydach.pl/blog/tags/regexp.html</link>
    <lastBuildDate>Tue, 25 Jan 2022 12:54:25 +0100</lastBuildDate>
    <description>Blog of Jacek Złydach - a programmer and science enthusiast.</description>
    <generator>Regenerate2</generator>
    <managingEditor>temporal.pl@gmail.com (Jacek Złydach)</managingEditor>
    <webMaster>temporal.pl@gmail.com (Jacek Złydach)</webMaster>
    <ttl>1440</ttl>
    <copyright>© 2017, 2018, 2019, 2020, 2021, 2022, Jacek Złydach</copyright>
    <item>
      <title>Usprawnij pracę z wyrażeniami regularnymi</title>
      <link>https://jacek.zlydach.pl/blog/2013-08-10-usprawnij-prace-z-wyrazeniami-regularnymi.html</link>
      <guid isPermaLink="true">https://jacek.zlydach.pl/blog/2013-08-10-usprawnij-prace-z-wyrazeniami-regularnymi.html</guid>
      <pubDate>Sat, 10 Aug 2013 04:49:35 +0200</pubDate>
      <category domain="https://jacek.zlydach.pl/blog/tags/old-blog.html">Old blog</category>
      <category domain="https://jacek.zlydach.pl/blog/tags/porady.html">porady</category>
      <category domain="https://jacek.zlydach.pl/blog/tags/php.html">PHP</category>
      <category domain="https://jacek.zlydach.pl/blog/tags/regexp.html">regexp</category>
      <category domain="https://jacek.zlydach.pl/blog/tags/debuggex.html">Debuggex</category>
      <description><![CDATA[Ah, dawno nic nie wrzucałem ;). Chciałbym się dziś podzielić dwoma poradami związanymi z <a href="https://en.wikipedia.org/wiki/Regular_expression">regex'ami (wyrażeniami regularnymi)</a>.

<strong>Komentarze w wyrażeniach regularnych</strong>
Szybkie pytanie: co dopasowuje poniższe wyrażenie regularne?
<pre><code lang="php">preg_match('/.*(?=.{6,})(?=.*\d)(?=.*[a-zA-Z]).*/', $something);</code></pre>

Myślę, że nawet <a href="http://xkcd.com/208/">wprawionemu w bojach</a> programiście przetworzenie tego wyrażenia zajmie krótką chwilę. W praktyce wyrażenia regularne szybko robią się skomplikowane, ekstremalnie  trudne w czytaniu i jeszcze trudniejsze w debugowaniu. Można jednak te problemy rozwiązać za pomocą odrobiny formatowania i dodania komentarzy.

<pre><code lang="php">preg_match('/^
            .*              # Match any number of characters...
            (?=.{6,})       # ... AND match at least 6 characters (lookahead) ...
            (?=.*\d)        # ... AND match one digit after any number of characters (lookahead) ...
            (?=.*[a-zA-Z])  # ... AND match one letter after any number of characters (lookahead) ...
            .*              # ... AND allow any number of characters later.
            $/x', $password);</code></pre>
Prawda, że czytelniej?

Modyfikator <code inline="inline">x</code> powoduje, że wszystkie niewyescape'owane białe znaki zostaną zignorowane, a wszystko od znaku <code inline="inline">#</code> do końca linii zostanie potraktowane jako komentarz i zignorowane.

O możliwości komentowania wyrażeń regularnych dowiedziałem się z artykułu <a href="http://net.tutsplus.com/tutorials/php/advanced-regular-expression-tips-and-techniques/">Advanced Regular Expression Tips and Techniques</a>. Zachęcam do jego przeczytania - jest tam więcej ciekawych porad, m.in. na temat callbacków czy nadawania nazw grupom.

<strong>Debugger do regexpów</strong>
Istnieje świetne narzędzie do analizy i debugowania regexpów - <a href="http://www.debuggex.com/">Debuggex</a>. Wyświetla on strukturę wyrażenia graficznie, pozwalając na analizę krok po kroku.

Spójrzmy na przykład z poprzedniej części tego posta:
<img src="old-blog/download/posts/regexp/regexp.png">

Prawdziwa potęga tego narzędzia objawia się przy bardzo złożonych wyrażeniach regularnych, jak na przykład:
<pre><code>^(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?:\w+:\w+@)?(?:(?:[-\w]+\.)+(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?::[\d]{1,5})?(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&amp;(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?$</code></pre>

Polecam skopiowanie powyższego wyrażenia do Debuggexa. Z jednej strony widać wyraźnie, jak ten regexp działa i do czego służy; z drugiej strony, sekcja <em>Some random matches</em> pokazuje, jak dziwne rzeczy przejdą przez nie poprawnie.

<strong>Uwaga o wydajności wyrażeń regularnych</strong>
Jak zapewne wielu programistów, którzy swą przygodę zaczęli od programowania gier, jestem sceptycznie nastawiony do wszelkich rzeczy pracujących na stringach. W szczególności, regexpy mogą na pierwszy rzut oka wydawać się wysoce niewydajne - ot skomplikowane dopasowanie tekstu zapisane w formie tekstu. Z pewnością napisanie tego w formie normalnego kodu - acz dłuższe - musi być wydajniejsze, prawda?

Otóż nie. Wyrażenia regularne są tak na prawdę programem - są (bardzo skondensowanym) opisem <a href="http://pl.wikipedia.org/wiki/Automat_sko%C5%84czony">maszyny stanów</a>, przez którą dopasowywany tekst stopniowo przechodzi, i jeśli uda się doprowadzić tę maszynę do stanu terminalnego, to dopasowanie jest uznawane za udane. Co to dokładnie znaczy? Polecam przyglądnąć się jeszcze raz rysunkom, które tworzy Debuggex - to dokładnie struktura tejże maszyny stanów. Taki system reprezentowany jest w programie w sposób bardzo wydajny; prawdopodobnie dużo wydajniejszy od naiwnego parsera, którego moglibyśmy napisać w niewielkiej liczbie linii kodu.

Co więcej, niektóre języki programowania (jak na przykład <a href="http://weitz.de/cl-ppcre/#create-scanner">Common Lisp</a> lub <a href="http://stackoverflow.com/questions/1720191/java-util-regex-importance-of-pattern-compile">Java</a>) pozwalają <em>prekompilować wyrażenia regularne</em> - jeżeli spodziewamy się, że dane wyrażenie regularne będzie używane wielokrotnie w czasie działania programu, to możemy zbudować maszynę stanów tylko raz (w przeciwieństwie do tworzenia jej na nowo za każdym razem, gdy chcemy dopasować jakiś napis), a następnie używać jej wszędzie tam, gdzie potrzebujemy korzystać z danego wyrażenia regularnego. Są nawet języki, które <a href="http://stackoverflow.com/questions/209906/compile-regex-in-php">kompilują regexpy automatycznie</a>.
]]></description>
    </item>
  </channel>
</rss>