2011-09-21 20 views
10

Część kodu, nad którym pracuję, wykorzystuje szereg wyrażeń regularnych do wyszukiwania prostych wzorów łańcuchów (np. Wzory takie jak "foo [0-9] {3,4} pasek"). Obecnie używamy skompilowanych statycznie wzorców Java, a następnie wywołujemy Pattern#matcher, aby sprawdzić, czy ciąg zawiera dopasowanie do wzorca (nie potrzebuję dopasowania, tylko wartość logiczna wskazująca, czy jest dopasowanie). Powoduje to zauważalną alokację pamięci, która ma wpływ na wydajność.Wysokowydajne, proste wyrażenia regularne Javy

Czy istnieje lepsza opcja dopasowania do wyrażenia regularnego Java, która jest szybsza lub przynajmniej nie przydziela pamięci za każdym razem, gdy przeszukuje ciąg w poszukiwaniu wzorca?

+0

co na temat http://download.oracle.com/javase/1.4.2/docs/api/java/lang/String.html#matches(java.lang.String) spowoduje zwrócenie wartości boolowskiej – ant

+3

@ c0mrade . mecze () działa tak samo jak Pattern.matches (, ), który robi to samo co Pattern.compile () .matcher () .matches() – Jared

+0

@Jared poprawne, ale on powiedział, że był przy użyciu wzoru/matcher nie string pasuje – ant

Odpowiedz

13

Wypróbuj metodę matcher.reset("newinputtext"), aby uniknąć tworzenia nowych matcherów za każdym razem, gdy wywołujesz PatternMatcher.

+4

To powinno w pewnym stopniu poprawić prędkość. Zobacz mój [wprawdzie słaby] test tutaj: http://pastie.org/2570213 – Jared

+0

@Jared: bardzo fajny test. –

+3

To dobrze, ale pamiętaj, że klasa Matchera nie jest bezpieczna dla wątków. W środowisku z wątkami zainicjuj Matchera dla każdego wątku lub po prostu użyj wstępnie skompilowanego statycznego wzorca (klasa Pattern jest wątkowo bezpieczna, ale daje ci ten sam problem z alokacją pamięci, od którego zacząłeś). – DavidMFrey

0

Można spróbować użyć statycznej metody Pattern.matches(), która właśnie zwróci wartość logiczną. To nie zwróci obiektu Matcher, aby mógł pomóc w problemach z alokacją pamięci.

Mimo to wzorzec regex nie byłby prekompilowany, więc byłby to wynik w stosunku do zasobów w danym momencie.

+5

'Pattern # matches' tworzy Matchera obiekt wewnątrz tego metamorfozy od. – jonderry

+0

@jonderry: Bardzo dobry punkt +1. W rzeczywistości tworzy on zarówno Pattern, kompilując wyrażenie regularne, jak i tworząc Mather dla danego wejścia. –

2

Jeśli chcesz uniknąć tworzenia nowego Matcher dla każdego wzoru, należy użyć metody usePattern(), tak:

Pattern[] pats = { 
    Pattern.compile("123"), 
    Pattern.compile("abc"), 
    Pattern.compile("foo") 
}; 
String s = "123 abc"; 
Matcher m = Pattern.compile("dummy").matcher(s); 
for (Pattern p : pats) 
{ 
    System.out.printf("%s : %b%n", p.pattern(), m.reset().usePattern(p).find()); 
} 

see the demo on Ideone

Musisz użyć reset() sposób dopasowujący za zbyt lub find() będzie wyszukiwać tylko od miejsca, w którym zakończył się poprzedni mecz (zakładając, że mecz się powiódł).

4

Jeśli oczekujesz mniej niż 50% linii pasujących do Twojego regex, można najpierw spróbować przetestować jakiegoś podciągu poprzez String.indexOf() która wynosi około 3 do 20 razy szybciej na prostej sekwencji w porównaniu do regex dopasowującego:

if (line.indexOf("foo")>-1) && pattern.matcher(line).matches()) { 
    ... 

Jeśli dodasz do swojego kodu taką heurystykę, pamiętaj, aby zawsze dobrze je dokumentować i sprawdź za pomocą profilera, że ​​kod jest rzeczywiście szybszy w porównaniu do prostego kodu.

+0

A także dodaj test, upewniając się, że zoptymalizowana wersja robi to samo lub zwykłe wyrażenie regularne. Jest to przydatne, gdy ktoś zmienia wyrażenie regularne i zapomina o reszcie. – maaartinus

+0

Dobra podpowiedź - działa to również w przypadku zawartości, a dopasowanie nie musi być idealne - wystarczy, że zmniejszysz liczbę elementów, które trafią do wzornika o co najmniej 50% – Tadhg

Powiązane problemy