Wyrażenia regularne
Transkrypt
Wyrażenia regularne
Zaawansowane techniki programowanie
Wyrażenia regularne
Plan wykładu
Podstawy teoretyczne wyrażeń regularnych
Elementy składowe – wzorce dopasowań
Wyrażenia regularne w językach programowania (Python)
Przykłady
Ćwiczenia
Co to są wyrażenia regularne?
Teoria (języki formalne):
Praktyka (języki programowania):
- ε reprezentuje {””}
- | to samo co +
- a reprezentuje {a}
- * to samo co *
- jeżeli r i s są w.r. reprezentującymi R i S,
to (r+s), (rs) i (r*) są
w.r. reprezentującymi
R ∪ S, RS i R*
- . dowolny znak
- r{m} m kopii r
- \s spacja, tab itp.
- i wiele innych
Rozbieżność teorii i praktyki
Wyrażenie regularne ^([ab]+)\1$ opisuje
zbiór niepustych słów które można przedstawić
jako ww, gdzie w ∈ {a, b}*.
Wyżej opisany język nie jest regularny (można
tego dowieść, korzystając z lematu o pompowaniu dla języków regularnych).
Język ten to: {aa, bb, abab, baba, aaaa, bbbb,
aaaaaa, aabaab, abaaba, ...}.
Złożoność obliczeniowa
Wszystkie algorytmy typu w.r. → słowa (takie
jak sprawdzanie, czy słowo należy do języka
generowanego przez w.r., wyszukiwanie wzorców itd.) są wielomianowe.
Każdy algorytmy typu słowa → w.r prawie na
pewno będzie wykładniczy.
Uzasadnienie złoż. wykładniczej
Dana Angluin [1976], An application of the
theory of computational complexity to the study
of inductive inference, (praca doktorska).
Twierdzenie. Dany jest skończony alfabet A,
skończone rozłączne zbiory słów X oraz Y.
Znalezienie minimalnego wyrażenia regularnego akceptującego wszystkie słowa z X i nieakceptującego żadnego słowa z Y jest problemem NP-trudnym.
Do czego służą w.r.?
Sprawdzanie poprawności danych wprowadzonych przez użytkownika
Wyszukiwanie danych w tekście
Zamiana w tekście wyrazów spełniających zadany wzorzec na inne wyrazy
Inne
Podstawowe wzorce dopasowań
abc odpowiada dokładnie słowu abc
a* ciąg zera lub większej liczby znaków a
b+ ciąg jednego lub większej liczby znaków b
ab?c słowo ac lub abc (? znaczy zero lub raz)
tak|nie tak lub nie (| oznacza alternatywę)
[abc] jeden ze znaków: a, b lub c
a{5} słowo aaaaa
b{2,5} od dwóch do pięciu znaków b
Ciąg dalszy wzorców dopasowań
. dowolny znak
^ początek łańcucha
$ koniec łańcucha
za pomocą \ można specjalny znak (jak *, +, $
itd.) traktować jak zwykły
(…) grupowanie i odwołanie wsteczne
\d cyfra
\w znak alfanumeryczny
Wyrażenia regularne w Pythonie
>>> import re
>>> m = re.search(r"[ab]+", "cabacda")
>>> print m.group(), m.start(), m.end()
aba 1 4
>>> wr = re.compile(r"[ab]+")
>>> m = wr.search("cabacda")
>>> print m.group(), m.start(), m.end()
aba 1 4
search() konra match()
>>> m = re.search(r"a*b{2,5}", "babb")
>>> print m.group(), m.start(), m.end()
abb 1 4
>>> m = re.match(r"a*b{2,5}", "babb")
>>> if m == None:
...
print "Nie pasuje!"
...
Nie pasuje!
Iterator po wszystkich podciągach
>>> wr = re.compile(r"\d+(\.\d+)?")
>>> tekst = "Liczby 2, 3.4 i 11.26."
>>> for s in wr.finditer(tekst):
...
...
2
3.4
11.26
print s.group(0)
Zastępowanie dopasowań
>>> wr = re.compile(r"\ba\w*")
>>> tekst = "Bak, as, 3a, kot, agat."
>>> nowy = wr.sub("*", tekst)
>>> print nowy
Bak, *, 3a, kot, *.
Zachłannie czy leniwie?
Rozważmy w.r a{2,3}
Pytanie: w tekście aaaaaa zostaną znalezione
trzy podciągi o długości dwa, czy dwa podciągi
o długości trzy?
W.r. są zachłanne
>>> wr = re.compile(r"a{2,3}")
>>> for s in wr.finditer("aaaaaa"):
...
print s.group(0),s.start(0),s.end(0)
...
aaa 0 3
aaa 3 6
Operatory leniwe
Operatory ?, * i + mają swoje leniwe odpowiedniki ??, *? i +?.
Dopasowują one jak najkrótszy możliwy ciąg
znaków w taki sposób, żeby dalsza część w.r.
też była dopasowana.
Przykład zachowania leniwego
>>> wr = re.compile(r"a{2}a??")
>>> for s in wr.finditer("aaaaaa"):
... print s.group(0),s.start(0),s.end(0)
...
aa 0 2
aa 2 4
aa 4 6
Kolejny przykład
>>> wr_l = re.compile(r"(.*?)([ab]+)")
>>> wr_z = re.compile(r"(.*)([ab]+)")
>>> tekst = "cababb"
>>> s_l = wr_l.match(tekst)
>>> print s_l.groups()
('c', 'ababb')
>>> s_z = wr_z.match(tekst)
>>> print s_z.groups()
('cabab', 'b')
Grupowanie i referencja
Każda para nawiasów (r) tworzy grupę, która
jest skojarzona z podciągiem pasującym do
wyrażenia regularnego r.
Grupy numerowane są kolejnymi liczbami całkowitymi od 1 włącznie.
Liczy się kolejność wystąpienia nawiasu otwierającego:
(a(b|c)d+)(e*) - występują trzy grupy.
Przykład grupowania
>>> wr = re.compile(r"(a(b|c)d+)(e*)")
>>> m = wr.match("acdd")
>>> print m.groups()
('acdd', 'c', '')
>>> print m.group(1)
acdd
Odwołania wsteczne
Do podciągu skojarzonego z grupą możemy się
odwołać w tym samym w.r. za pomocą wyrażenia \nr_grupy.
Odwołania takie mogą również znajdować się
w łańcuchu zastępującym (w funkcji sub()).
Przykład referencji
>>> wr = re.compile(r"(\w+)\1")
>>> tekst = "abc bb aaaa ccc"
>>> for m in wr.finditer(tekst):
...
...
bb
aaaa
cc
print m.group()
Ćwiczenie kontrolne
>>> wr = re.compile(r"(\w+?)\1")
>>> tekst = "abc bb aaaa ccc"
>>> for m in wr.finditer(tekst):
...
...
?
print m.group()
Dodatkowe wzorce dopasowań
(?:r) nawiasy w wersji niegrupującej
(?=r) wyprzedzenie niekonsumujące
(?!r) wyprzedzenie niekonsumujące negatywne
(?<=r) sprawdzenie wsteczne niekonsumujące
(?<!r) sprawdzenie wsteczne niekonsumujące
negatywne
W dwóch ostatnich przypadkach r musi mieć stałą długość
Przykłady dodatkowych wzorców
>>> m = re.search(r"a(?=c)", "abacad")
>>> print m.group(), m.start()
a 2
>>> wr = re.compile(r"a(?!c)")
>>> for m in wr.finditer("abacad"):
...
...
a 0
a 4
print m.group(), m.start()
Unicode
>>> wzorzec = ur"(?<!koń)\s+słoń"
>>> wr = re.compile(wzorzec, re.U)
>>> t = u"koń słoń osa słoń"
>>> for m in wr.finditer(t):
...
print m.group(), m.start()
...
słoń 12
Ćwiczenie 1
>>> wr = re.compile(r"(?<=\d)\.(\d)\d*")
>>> t = "Ala ma: 3, 2.999, a.902"
>>> nowy = wr.sub(r",\1", t)
>>> print nowy
?
Ćwiczenie 2 (True czy False)
>>> re.search(r"^a*$", "aaabb") != None
?
>>> re.search(r"\Bn", "t i n") != None
?
>>> re.search(r"\bn", "t i n") != None
?
>>> wr = re.compile(r"a+?.+?b+")
>>> wr.search("baaabaabba").group()
?
Ćwiczenie 3
>>> w=r"^(?=.*\d)(?=.*[a-zA-Z]).{8,15}$"
>>> wr = re.compile(w)
>>> S = ["a1A", "aaa111AAA", "AAAAaaaa",
... "aaaaa1234567890ABCDEF", "12aaaa12"]
>>> for s in S:
...
if wr.match(s): print "Tak",
...
else: print "Nie",
...
?
Ćwiczenie 4
>>> wr = re.compile(r"(\w(\s)?)+")
>>> wr.search(" ba aab
ba").group()
?
>>> wr = re.compile(r"(\w(\s)?)+?")
>>> wr.search(" ba aab
?
ba").group()
Podsumowanie
Wyrażenia regularne należą do najbardziej rozpowszechnionych narzędzi informatycznych
Można skorzystać z bazy danych w.r. znajdującej się pod adresem http://regexlib.com/