Een overzicht van de prestaties van reguliere expressies in Java

1. Overzicht

In deze korte tutorial laten we zien hoe de engine voor patroonafstemming werkt. We zullen ook verschillende manieren presenteren om te optimaliseren normale uitdrukkingen in Java.

Voor een inleiding in het gebruik van normale uitdrukkingen, verwijzen wij u naar dit artikel hier.

2. De engine voor patroonafstemming

De java.util.regex pakket gebruikt een type engine voor patroonaanpassing genaamd een Niet-deterministische eindige automaat (NFA). Het is overwogen niet-deterministisch omdat bij het zoeken naar een overeenkomst met een reguliere expressie voor een bepaalde string, elk teken in de invoer meerdere keren kan worden gecontroleerd aan de hand van verschillende delen van de reguliere expressie.

Op de achtergrond gebruikt de bovengenoemde motor teruggaan. Dit algemene algoritme probeert alle mogelijkheden uit te putten totdat het een mislukking verklaart. Beschouw het volgende voorbeeld om het NFA:

"tra (vel | ce | de) m"

Met de input Draadreizen", Zal de motor eerst zoeken naar"tra”En vind het onmiddellijk.

Daarna zal het proberen te matchen "vel”Beginnend bij het vierde teken. Dit komt overeen, dus het zal doorgaan en proberen te matchen "m“.

Dat komt niet overeen, en daarom keert het terug naar het vierde teken en zoekt het naar 'ce“. Nogmaals, dit komt niet overeen, dus het keert weer terug naar de vierde positie en probeert met "de“. Die string komt ook niet overeen, en gaat dus terug naar het tweede teken in de invoertekenreeks en probeert te zoeken naar een andere "tra“.

Bij de laatste fout zal het algoritme een fout retourneren.

Met het eenvoudige laatste voorbeeld moest de motor verschillende keren teruggaan om de invoer te matchen Draad naar de reguliere expressie. Daarom, het is belangrijk om de hoeveelheid backtracking die het doet te minimaliseren.

3. Manieren om te optimaliseren Normale uitdrukkingen

3.1. Vermijd hercompilatie

Reguliere expressies in Java worden gecompileerd in een interne datastructuur. Deze compilatie is een tijdrovend proces.

Elke keer dat we het String.matches (String-regex) methode, wordt de opgegeven reguliere expressie opnieuw gecompileerd:

if (input.matches (regexPattern)) {// doe iets}

Zoals we kunnen zien, wordt elke keer dat de voorwaarde wordt geëvalueerd, de regex-expressie gecompileerd.

Om te optimaliseren is het mogelijk om eerst het patroon te compileren en vervolgens een Matcher om de toevalligheden in de waarde te vinden:

Patroonpatroon = Pattern.compile (regexPattern); for (String-waarde: waarden) {Matcher matcher = pattern.matcher (waarde); if (matcher.matches ()) {// doe iets}}

Een alternatief voor de bovenstaande optimalisatie is hetzelfde Matcher instantie met zijn resetten () methode:

Patroonpatroon = Pattern.compile (regexPattern); Matcher matcher = pattern.matcher (""); for (String-waarde: waarden) {matcher.reset (waarde); if (matcher.matches ()) {// doe iets}}

Vanwege het feit van Matcher is geen thread-safe, we moeten voorzichtig zijn met het gebruik van deze variatie. Het kan waarschijnlijk gevaarlijk zijn in scenario's met meerdere threads.

Samenvattend: in elke situatie waarin we zeker weten dat er maar één gebruiker van het Matcher op elk moment is het OK om het opnieuw te gebruiken resetten. Voor de rest is het voldoende om de voorgecompileerde versie opnieuw te gebruiken.

3.2. Werken met afwisseling

Zoals we zojuist in het vorige gedeelte hebben gecontroleerd, kan het onvoldoende gebruik van wisselingen schadelijk zijn voor de prestaties. Het is belangrijk om opties die waarschijnlijker voorkomen vooraan te plaatsen, zodat ze sneller kunnen worden gematcht.

We moeten ook gemeenschappelijke patronen ertussen extraheren. Het is niet hetzelfde om te zeggen:

(reizen | handel | traceren)

Dan:

tra (vel | de | ce)

Dit laatste is sneller omdat de NFA zal proberen te matchen "tra”En zal geen van de alternatieven proberen als het het niet kan vinden.

3.3. Groepen vastleggen

Elke keer dat we groepen vangen, lopen we een kleine tijdstraf op.

Als we de tekst in een groep niet hoeven vast te leggen, moeten we het gebruik van niet-vastleggende groepen overwegen. In plaats van '(M)", gebruik alstublieft "(?: M)“.

4. Conclusie

In dit korte artikel hebben we kort opnieuw bekeken hoe NFA werken. Vervolgens hebben we onderzocht hoe we de prestaties van onze reguliere expressies konden optimaliseren door onze patronen vooraf te compileren en een Matcher.

Ten slotte wezen we op een aantal overwegingen waarmee we rekening moeten houden bij het werken met afwisselingen en groepen.

Zoals gewoonlijk is de volledige broncode te vinden op GitHub.