2013-03-08 23 views
11

Załóżmy, że mam listę wyrażeń regularnych (odczyt z zewnętrznego źródła - pliku, bazy danych itp.). Chcę sprawdzić, który z tych wyrażeń regularnych pasuje do ciągu.Łączenie wielu wyrażeń regularnych w jeden automat

Mogę utworzyć iterację poprzez wszystkie te wyrażenia regularne i dopasować je, ale lista może być ogromna i jest to operacja krytyczna.

Mogę łączyć wszystkie te wyrażenia regularne w jeden (z | między nimi), ale problem polega na tym, że mogę zidentyfikować tylko pierwsze dopasowane wyrażenie regularne, nie wszystkie.

Innym pomysłem może być stworzenie automatu do wszystkich tych wyrażeń regularnych i oznaczenie stanów końcowych za pomocą, powiedzmy, indeksów odpowiedniego wyrażenia regularnego. Patrzyłem na http://cs.au.dk/~amoeller/automaton/, bibliotekę, która wydaje się być zdolna do pracy z wyrażeń regularnych i automatu, ale nie jestem pewien, czy można ją rozszerzyć, aby rozwiązać mój problem.

Czy masz jakieś inne pomysły?

Aby wyjaśnić niektóre komentarze, dodałem przykładowy kod:

import java.util.regex.Matcher; 
import java.util.regex.Pattern; 


public class PatternTest { 
    public static void main(String[] args) { 
     Pattern p = Pattern.compile("(a(?:b|c)a)|((?:a|b)ba)|(ab(?:a|c))");  
     Matcher m = p.matcher("aba"); 
     System.out.println(m.matches()); 
     System.out.println(m.groupCount()); 
     for (int i = 0, n = m.groupCount(); i < n; i++) { 
      System.out.println(m.group(i)); 
     } 
    } 
} 

wypisze

true 
3 
aba 
aba 
null 

Jak widać tylko pierwsza grupa jest dopasowana i nie widzę sposób na dopasowanie dwóch pozostałych.

Więcej wyników - Wykorzystanie powyższej biblioteki automatów sprawi, że problem zmniejszy się do następujących: w przypadku łączenia dwóch lub więcej automatów, w jaki sposób można zidentyfikować końcowy stan odpowiadający oryginalnym automatom?

+0

Czy rozważałeś dodanie nazwanych grup do każdego wyrażenia ed? Możesz sprawdzić, które z nich pasują do tego. –

+0

Te brzmią jak opcje dostępne dla Javy. W Perlu byłoby łatwiej. Możesz po prostu zamiennie wszystkie wyrażenia, a na końcu każdego wyrażenia (zwanego 'X') dodaj na przykład' (? {$ Dopasowane {X} = 1}) (?!) '. Który oznacza wyrażenie "X" jako dopasowane, a następnie kończy się niepowodzeniem, umożliwiając dopasowanie także innych wyrażeń. (Aby ją zoptymalizować, możesz także umieścić każde wyrażenie w grupie przechwytywania atomów.) – Qtax

+0

@ Michael: Tak, również to rozważałem. Problem polega na tym, że wyrażenie regularne w Javie pasuje tylko do pierwszej grupy (nazwanej lub nienazwanej). –

Odpowiedz

2

dk.brics.automaton nie obsługuje tego bezpośrednio, ale można uogólnić reprezentację automatów (i odpowiednich operacji automatów), aby rozróżnić różne rodzaje stanów akceptowania. Zacznij od dodania pola int, na przykład, do klasy State i użyj tego pola, gdy ustawisz "Akceptuj".

2

Na ostateczną odpowiedź (jeśli istnieje) trzeba będzie trochę więcej informacji, takich jak:

  1. Mówisz lista regexes jest ogromna; czy mógłbyś to sprecyzować? Tysiące? Miliony? Miliardy i miliardy?

  2. Kto napisał te wyliczenia i czy wiedzą, co robią? Czy wyrażenia regularne są dokładnie testowane (dla poprawności działania i) przed dodaniem do listy?

  3. W kodzie przykładowym używana jest metoda matches(), która wymaga wyrażenia regularnego do opisania całego ciągu znaków. Działa tak, jakby regex był rzeczywiście
    \A(?:(a(?:b|c)a)|((?:a|b)ba)|(ab(?:a|c)))\z
    który pasuje do "aba", ale nie "aaba" lub "abaa". Jeśli używałeś wyrażeń regularnych w innych narzędziach lub językach przed pojawieniem się na Javie, to zachowanie może Cię zaskoczyć. Tradycyjnie, ciąg zawsze był "dopasowywany" do wyrażenia regularnego, jeśli wyrażenie opisuje dowolny ciąg znaków 10, nawet podłańcuch o zerowej długości. Aby uzyskać to zachowanie w Javie, musisz użyć metody Matchera find().

  4. Czy są jakieś wspólne czynniki, które można usunąć ze wszystkich wyrażeń regularnych na liście, takich jak minimalna lub maksymalna długość, wspólne podciągi lub wspólne podzbiory znaków? Na przykład dowolny ciąg, który pasuje do jednego z przykładowych wzorców, musi również pasować do [abc]{3}. Jeśli tak, możesz chcieć utworzyć filtry na ich podstawie (być może wyrecytować, a może nie), zanim uruchomisz poważne dopasowywanie.(Nie polecam tego, jeśli były przy użyciu Perl, która choc-a-blok z optymalizacje jak to już, ale Java nie jest zbyt dumny, by przyjąć trochę pomocy. ☺)

Ale czuję się całkiem bezpieczne radzenie ci, abyś używał oddzielnych wyrażeń regularnych zamiast łączenia ich wszystkich w jeden. Frankenregex niekoniecznie musi działać lepiej, a rozwiązywanie problemów byłoby koszmarem! Można wstępnie skompilować wszystkie obiekty wzór, można utworzyć obiekt Matcher wyprzedzeniem i używać go na wszystkich meczach, tak jak poniżej:

m.reset(s).usePattern(p); 

Oto demo. Nie mogę zrobić żadnych gwarancji (na razie jesteś na łasce tego, kto napisał wyrażeń regularnych), ale jeśli rozwiązanie jest możliwe, myślę, że jest to najbardziej obiecujące podejście.

+0

Świetna odpowiedź. Być może dlatego, że tak właśnie myślałem, ale dodane demo było dobre i dowiedziałem się o funkcjach reset (x), których wcześniej nie brałem pod uwagę. – Omertron

Powiązane problemy