Blog programisty w Oracle PL/SQL

Jest to blog eksperymentatora programisty w PL/SQL dla Oracle. Wszystkie kody tutaj zamieszczone mogą być dowolnie wykorzystywane i zmieniane. A jeśli Ktoś z Gości znajdzie błąd, będę niezwykle wdzięczny...
Zapisz

Szukaj na tym blogu

sobota, 7 grudnia 2013

Wyszukiwanie z wykorzystaniem wyrażeń regularnych

Wyszukiwanie  wzorów określonych za pomocą wyrażeń  regularnych umożliwia  znaczące uproszczenie algorytmów do przetwarzania tekstów. Różnica polega na tym, że definiujemy wzorzec wyszukiwania, testujemy i ciągle  udoskonalamy..   Podczas rozpoczynania pracy z wyrażeniami regularnymi, okazuje się, że największym problemem jest prawidłowe zdefiniowanie wzorca.
Przeanalizujmy następujący przykład - w tekście wyszukujemy kod pocztowy..
Wyrażenie opisujące kod pocztowy z myślnikiem ma następującą postać:
[[:digit:]]{2}-[[:digit:]]{3} - to oznacza  wzorzec składający się z dwóch dowolnych cyferek, znaku myślnika i trzech cyferek. Ale czy działa on poprawnie??
SELECT REGEXP_SUBSTR('Mój kod pocztowy to 02-768', '[[:digit:]]{2}-[[:digit:]]{3}',1 ,1) FROM DUAL  --to polecenie wykona się poprawnie

SELECT REGEXP_SUBSTR('Mój telefon to to 502-559-373', '[[:digit:]]{2}-[[:digit:]]{3}',1 ,1) FROM DUAL  --to polecenie zwróci 02-559 i  bedzie przeszukiwać nr telefonów, nr kont. 
Jeśli mamy  wejściowy plik z adresami, to z reguły powinno wystarczyć. W innych przypadkach proponuję zastosowanie poniższego wzorca
(^|[[:blank:]])[[:digit:]]{2}-[[:digit:]]{3}($|[[:blank:]])
(^|[[:blank:]]) to podwyrażenie oznacza, że wzorzec powinien zaczynać się od początku  przeszukiwanego tekstu lub powinien być poprzedzony  znakiem niewidocznym, np. spacją
($|[[:blank:]])- to podwyrażenie oznacza, że wzorzec ma się kończyć na ostatnim znaku przeszukiwanego tekstu lub znakiem niewidocznym np. spacją

SELECT REGEXP_SUBSTR('Mój telefon to to 502-559-373', '(^|[[:blank:]])[[:digit:]]{2}-[[:digit:]]{3}($|[[:blank:]])',1 ,1) FROM DUAL  --teraz wyrażenie zadziała poprawnie i nic nie zwróci, dobrze są opisane warunki brzegowe
Często nie da się stworzyć ogólnego wzorca dla poszukiwanych danych, ale można znacznie uprościć mechanizmy wyszukiwania. Dużo łatwiej jest modyfikować wyrażenia regularne niż kod do analizy zawartości tekstu.

Poniżej inne ciekawe przykłady:
Wyszukiwanie adresu poczty elektroniczne:
SELECT REGEXP_SUBSTR( 'Mój mail to szkaradnik@gmail.com', '[[:alpha:]]([[:alnum:]]|\.){0,30}[[:alnum:]]@[a-zA-Z0-9._%-]+\.[[:alpha:]]{2,4}') FROM DUAL;

SELECT REGEXP_SUBSTR( 'Nie jestem posiadaczem adresu Kazimierz.Szpyt@gmail.com.pl - bo nie istnieje', '[[:alpha:]]([[:alnum:]]|\.){0,30}[[:alnum:]]@[a-zA-Z0-9._%-]+\.[[:alpha:]]{2,4}') FROM DUAL;

Wyszukiwanie  inicjałów z nazwiskiem, przy założeniu, że po inicjałach imion sa kropki, litery inicjałów i pierwsza litera nazwiska są duże:
SELECT REGEXP_SUBSTR( 'To nie sa moje inicjały to  A.   D.  C.Kowalski  :) ', '([[:upper:]]{1}.{1}[[:blank:]]*)+[[:upper:]]{1}[[:lower:]]+', 1,1)  FROM DUAL;
Należy zwrócić uwagę, że powyższe wyrażenie nie  działa poprawnie dla np. nazwisk dwuczłonowych

Wykrywanie podstringów w formacie DDDDSDDSDD,  DDDDSDSD, DDDDSDSDD lub DDDDSDDSD, gdzie D to cyfra, a S to separator w postaci któregoś z ukośników lub myślnika
SELECT REGEXP_SUBSTR('Co to za data 2000\11/23','[[:digit:]]{4}((\\)|-|/)[[:digit:]]{1,2}((\\)|-|/)[[:digit:]]{1,2}', 1, 1 ) FROM DUAL;
To wyrażenie jest pomocne w wykrywaniu wyrażeń będących datami.

Określanie, czy dany  tekst jest o charakterze politycznym (zwracanie liczby wystąpień nazw partii w tekście)
SELECT REGEXP_COUNT( 'POlityczni POpaprańcy POpierający POlityczną POprawność i POdnoszący POdatki', 'PO|PiS|SLD|PSL', 1 ) FROM DUAL;

Wyszukiwanie w danym tekście kolejnych zdań(niepustych tekstów rozdzielonych kropkami)
DECLARE
 vc_Wyrazenie VARCHAR2(75 CHAR)  := 'Ala  ma kota, psa i papugę.Ala ma także X-Box-a. Ala ma mnóstwo lalek.';
 vn_Liczba NUMBER;
 BEGIN
  vn_Liczba := REGEXP_COUNT ( vc_Wyrazenie,'[^(\.)]+');
   FOR el IN 1.. vn_Liczba
   LOOP
     DBMS_OUTPUT.PUT_LINE( REGEXP_SUBSTR ( vc_Wyrazenie,'[^(\.)]+', 1, el));
   END LOOP;
 END;
Zauważmy, że powyższy kod ma następujące ograniczenia:
  • zwracane zdania nie kończą się kropką
  • kod nie radzi sobie ze skrótowcami zawierającymi kropki, z inicjałami, do obróbki tekstów biorokratycznych sie nie nadaje, ale z beletrystyką poradzi sobie całkiem dobrze
Instrukcja REGEXP_SUBSTR nadaje się  do parsowania tekstu z separatorami ale pod warunkiem, że wszystkie tokeny są niepuste lub pozycja poszczególnego tokena jest bez znaczenia.
Poprawnie zostanie przeprocesowany tekst:
Kazimierz|Szpyt|śpioch|łasuch|
natomiast  poniższy tekst zostanie rozbity na tokeny, ale bez pustych tokenów
Kazimierz||Szpyt||||śpioch|łasuch|

Brak komentarzy:

Prześlij komentarz