Zanurkuj w Pythonie/Rozwlekłe wyrażenia regularne

Rozwlekłe wyrażenia regularne

edytuj

Jak na razie, mieliśmy do czynienia z czymś, co nazywam "zwięzłymi" wyrażeniami regularnymi. Jak pewnie zauważyliśmy, są one trudne do odczytania i nawet, jeśli już je rozszyfrujemy, nie ma gwarancji, że zrobimy to za np. sześć miesięcy. To, czego potrzebujemy, to dokumentacja w ich treści.

Python pozwala na to przez tworzenie rozwlekłych wyrażeń regularnych (ang. verbose regular expressions). Różnią się one od zwięzłych dwoma rzeczami:

  • Białe znaki są ignorowane. Spacje, znaki tabulacji, znaki nowej linii nie są dopasowywane jako spacje, znaki tabulacji lub znaki nowej linii. Znaki te nie są w ogóle dopasowywane. (Jeśli byśmy chcieli jednak dopasować któryś z nich, musisz poprzedzić je odwrotnym ukośnikiem (\).)
  • Komentarze są ignorowane. Komentarz w rozwlekłym wyrażeniu regularnym wygląda dokładnie tak samo, jak w kodzie Pythona: zaczyna się od # i leci aż do końca linii. W tym przypadku jest to komentarz w wieloliniowym łańcuchu znaków, a nie w kodzie źródłowym, ale zasada działania jest taka sama.

Łatwiej będzie to zrozumieć jeśli skorzystamy z przykładu. Skorzystajmy ze zwięzłego wyrażenia regularnego, które utworzyliśmy wcześniej i zróbmy z niego rozwlekłe. Ten przykład pokazuje jak.

Przykład. Wyrażenia regularne z komentarzami
>>> pattern = """
    ^                   # początek łańcucha znaków
    M{0,3}              # tysiące - 0 do 3 M
    (CM|CD|D?C{0,3})    # setki - 900 (CM), 400 (CD), 0-300 (0 do 3 C),
                        #        lub 500-800 (D, a po nim 0 do 3 C)
    (XC|XL|L?X{0,3})    # dziesiątki - 90 (XC), 40 (XL), 0-30 (0 do 3 X),
                        #        lub 50-80 (L, a po nim 0 do 3 X)
    (IX|IV|V?I{0,3})    # jedności - 9 (IX), 4 (IV), 0-3 (0 do 3 I),
                        #        lub 5-8 (V, a po nim 0 do 3 I)
    $                   # koniec łańcucha znaków
    """
>>> re.search(pattern, 'M', re.VERBOSE)                #(1)
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MCMLXXXIX', re.VERBOSE)        #(2)
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MMMDCCCLXXXVIII', re.VERBOSE) #(3)
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'M')                            #(4)
  1. Najważniejszą rzeczą o której należy pamiętać, gdy korzystamy z rozwlekłych wyrażeń regularnych jest to, że musimy przekazać dodatkowy argument: re.VERBOSE. Jest to stała zdefiniowana w module re, która sygnalizuje, że wyrażenie powinno być traktowane jako rozwlekłe. Jak widzimy, ten wzorzec ma mnóstwo białych znaków (które są ignorowane) i kilka komentarzy (które też są ignorowane). Gdy usuniemy białe znaki i komentarze, to pozostanie dokładnie to samo wyrażenie regularne, jakie otrzymaliśmy w poprzednim przykładzie, ale o wiele mniej czytelne. (Zauważmy, że co prawda łańcuch znaków posiada polskie znaki, ale nie tworzymy go w unikodzie, ponieważ i tak te znaki nie mają dla nas żadnego znaczenia, ponieważ są w komentarzach.)
  2. To dopasowuje początek łańcucha, potem jedno z trzech możliwych M, potem CM, L i trzy z trzech możliwych X, a następnie IX i koniec łańcucha.
  3. To dopasowuje początek łańcucha, potem trzy z trzech możliwych M, dalej D, trzy z trzech możliwych C, L z trzema możliwymi X, potem V z trzema możliwymi I i na koniec koniec łańcucha.
  4. Tutaj nie udało się dopasować niczego. Czemu? Ponieważ nie przekazaliśmy flagi re.VERBOSE, więc funkcja re.search traktuje to wyrażenie regularne jako zwięzłe, z dużą ilością białych znaków i kratek. Python nie rozpoznaje samodzielnie, czy każemy mu dopasować zwięzłe, czy może rozwlekłe wyrażenie regularne i przyjmuje, że każde jest zwięzłe, chyba że wyraźnie wskażemy, że tak nie jest.