14 KiB
Języki formalne i złożoność obliczeniowa
Część 1: Wprowadzenie do biblioteki re
Wyrażenia regularne będziemy robić na podstawie języka python3. Dokumentacja: https://docs.python.org/3.11/library/re.html
Użyteczna strona do testowania wyrażeń regularnych: https://regex101.com/
Podstawowe funkcje
Match
match - zwraca dopasowanie od początku stringa
import re
wynik = re.match(r'Ala', 'Ala ma kota. Kot ma Alę.')
print(wynik)
print(f"Dopasowano: '{wynik.group()}', początek: {wynik.start()}, koniec: {wynik.end()}")
Search
search - zwraca pierwsze dopasowanie w napisie
# search
wynik = re.search(r'ni', 'Może nie najtaniej, ale jako tako.')
if wynik:
print(f"Dopasowano: '{wynik.group()}', początek: {wynik.start()}, koniec: {wynik.end()}")
else:
print('Nie znaleziono szukanego ciągu znaków.')
tekst = "Kto zakłóca ciszę nocną musi ponieść karę: 100 batów!"
wzorzec = re.compile('ci')
wynik = wzorzec.search(tekst)
if wynik:
print(f"Dopasowano: '{wynik.group()}', początek: {wynik.start()}, koniec: {wynik.end()}")
else:
print('Nie znaleziono szukanego ciągu znaków.')
findall
findall - zwraca listę wszystkich dopasowań
# findall
wzorzec = re.compile('o')
wynik = wzorzec.findall(tekst)
if len(wynik) > 0:
print(type(wynik))
print(f'Dopasowano: {wynik}')
else:
print(f'{wynik} Nie znaleziono szukanego ciągu znaków.')
wzorzec = re.compile('o')
wynik = wzorzec.finditer(tekst)
for w in wynik:
print(type(w))
print(f'Dopasowano: "{w.group()}", początek: {w.start()}, koniec: {w.end()}')
_Uwaga: pobierając z iteratora elementy, usuwamy je, nie można zatem ponownie się do nich odwołać.
Użycie przedrostka 'r' w wyrażeniach regularnych w Pythonie
W Pythonie przedrostek r
przed łańcuchem znaków oznacza "surowy" łańcuch znaków (ang. _raw string). Dlaczego jest to przydatne w kontekście wyrażeń regularnych?
Wyrażenia regularne często używają znaków specjalnych, takich jak \d
, \w
, \b
itp. W standardowych łańcuchach znaków w Pythonie, znaki te mają specjalne znaczenie. Na przykład, \t
oznacza tabulację, a \n
oznacza nową linię.
Jeśli chcemy użyć takiego wyrażenia regularnego w Pythonie, musielibyśmy podwajać znaki ukośnika, aby uniknąć konfliktu z wbudowanymi sekwencjami ucieczki w łańcuchach znaków, np. \\\\d
, \\\\w
.
# Bez użycia surowego łańcucha znaków
result1 = re.findall("\\\\d+", "123 abc 456")
print(result1)
# Używając surowego łańcucha znaków
result2 = re.findall(r"\d+", "123 abc 456")
print(result2)
sub
Kolejną użyteczną metodą jest sub(), która pozwala na zmianę wzorca na inny ciąg znaków:
wzorzec = re.compile(r'ni')
zmieniony = wzorzec.sub('Ni!', tekst)
print(zmieniony)
split
split - dzieli napis na podstawie wzorca
wzorzec = re.compile(r' ')
wynik = wzorzec.split(tekst)
print(f'Uzyskano {len(wynik)} wyniki/ów.')
for w in wynik:
print(w)
Metaznaki
[] - zbiór znaków
. - jakikolwiek znak
^ - początek napisu
$ - koniec napisu
? - znak występuje lub nie występuje
* - zero albo więcej pojawień się
+ - jeden albo więcej pojawień się
{} - dokładnie tyle pojawień się
| - lub
() - grupa
\ - znak ucieczki
\d digit
\D nie digit
\s whitespace
\S niewhitespace
wzorzec = re.compile(r'.')
print(wzorzec.findall(tekst))
# jeden lub wiecej / zero lub wiecej
tekst = "BCAAABGTAABBBCCTTSAGG4324242"
print(f'Łańcuch: {tekst}')
wzorzec = re.compile(r'X+')
print(f'Jeden lub więcej X: {wzorzec.findall(tekst)}')
wzorzec = re.compile(r'X*')
print(f'Zero lub więcej X: {wzorzec.findall(tekst)}')
# zero lub jeden
print(f'Łańcuch: {tekst}')
wzorzec = re.compile(r'.?')
print(wzorzec.findall(tekst))
wzorzec = re.compile(r'.?T')
print(wzorzec.findall(tekst))
print(f'Łańcuch: {tekst}')
wzorzec = re.compile(r'A+')
print(f'Dopasowanie zachłanne: {wzorzec.findall(tekst)}')
wzorzec = re.compile(r'A+?')
print(f'Dopasowanie leniwe: {wzorzec.findall(tekst)}')
print(f'Łańcuch: {tekst}')
# dokladnie 3 dopasowania
wzorzec = re.compile(r'A{3}')
print(f'{wzorzec.findall(tekst)}')
# pomiedzy 2 i 3 dopasowania
wzorzec = re.compile(r'A{2,3}')
print(f'{wzorzec.findall(tekst)}')
# 2 lub wiecej dopasowan
wzorzec = re.compile(r'A{2,}')
print(f'{wzorzec.findall(tekst)}')
# 3 lub mniej dopasowan
wzorzec = re.compile(r'A{,3}')
print(f'{wzorzec.findall(tekst)}')
# poczatek lub koniec lancucha
tekst = "Ale pięknie pachnie! Maciek, co gotujesz?"
# poczatek lancucha
wzorzec = re.compile(r'^Ale')
print(f'{wzorzec.findall(tekst)}')
# poczatek lancucha
wzorzec = re.compile(r'^ale')
print(f'{wzorzec.findall(tekst)}')
# koniec lancucha
wzorzec = re.compile(r'Ale$')
print(f'{wzorzec.findall(tekst)}')
# koniec lancucha + znak ucieczki
wzorzec = re.compile(r'gotujesz\?$')
print(f'{wzorzec.findall(tekst)}')
# grupy znakow
wzorzec = re.compile(r'[A-Z]')
print(f'Duże litery: {wzorzec.findall(tekst)}')
wzorzec = re.compile(r'[a-z]')
print(f'Małe litery: {wzorzec.findall(tekst)}')
wzorzec = re.compile(r'[A-z]')
print(f'Małe i duże litery: {wzorzec.findall(tekst)}')
wzorzec = re.compile(r'[aeiou]')
print(f'Samogłoski: {wzorzec.findall(tekst)}')
# wzorzec(?=X) - dopasowanie, jeśli po nim występuje X
tekst = "ACABADAHSAIIIQIIINSAODIANSAAGAGAGGGGPAAG"
print(f'Łańcuch: {tekst}')
wzorzec = re.compile(r'G+(?=A)')
print(f'{wzorzec.findall(tekst)}')
tekst = "Ale się zrobiła świąteczna atmosfera."
wzorzec = re.compile(r'Ale|świąteczna|zrobiła')
print(f'{wzorzec.findall(tekst)}')
Znaki specjalne
\s - biały znak
\S - nie-biały znak
\d - cyfra
\D - nie-cyfra
\w - znaki alfanumeryczne (litery i cyfry) oraz
\W - znaki nie-alfanumeryczne i nie
\b - początek lub koniec ,,słowa’’
\B - nie początek lub koniec ,,słowa''
[a-z] - małe litery
[A-Z] - wielkie litery
[0-9] - cyfry
wzorzec = re.compile(r'\w+')
print(f'Wyrazy: {wzorzec.findall(tekst)}')
Część II: Zadania praktyczne
Zadanie 1: Wyszukiwanie numerów telefonu
Napisz wyrażenie regularne, które znajdzie wszystkie numery telefonu w tekście. Zakładamy, że numer telefonu ma format xxx-xxx-xxx
lub xxx xxx xxx
.
tekst = """
Jan: 123-456-789
Anna: 987 654 321
Karol: 456-789-123
Zbyszek: 53252525342252
Tytus: aaaa666432
"""
wzorzec = re.compile(r"")
print(f'Numery: {wzorzec.findall(tekst)}')
tekst = """
jan.kowalski@gmail.com
anna.zielinska@amu.edu.pl
karol.nowak@interia.pl
hello world
@test.pl
x@x
fff22@gmail.com
"""
wzorzec = re.compile(r"")
print(f'Adresy: {wzorzec.findall(tekst)}')