Programmering

Regulære udtryk i Java, del 1: Mønstertilpasning og Mønsterklassen

Java's karakter og forskellige strengklasser tilbyder understøttelse på lavt niveau til mønstermatchning, men den understøttelse fører typisk til kompleks kode. For enklere og mere effektiv kodning tilbyder Java Regex API. Denne tutorial i to dele hjælper dig med at komme i gang med regulære udtryk og Regex API. Først pakker vi de tre magtfulde klasser, der er bosiddende i java.util.regex pakke, så udforsker vi Mønster klasse og dens sofistikerede mønster-matchende konstruktioner.

download Hent koden Download kildekoden for eksempel applikationer i denne vejledning. Oprettet af Jeff Friesen til JavaWorld.

Hvad er regulære udtryk?

EN almindelig udtryk, også kendt som en regex eller regexp, er en streng, hvis mønster (skabelon) beskriver et sæt strenge. Mønsteret bestemmer, hvilke strenge der hører til sættet. Et mønster består af bogstavelige tegn og metategn, som er tegn, der har særlig betydning i stedet for en bogstavelig betydning.

Mønster matching er processen med at søge tekst for at identificere Tændstikkereller strenge, der matcher et regex-mønster. Java understøtter mønstermatchning via sin Regex API. API'en består af tre klasser -Mønster, Matcherog MønsterSyntaksundtagelse- alle ligger i java.util.regex pakke:

  • Mønster genstande, også kendt som mønstre, er kompilerede regexes.
  • Matcher genstande eller matchere, er motorer, der fortolker mønstre til at finde matches i karakter sekvenser (objekter hvis klasser implementerer java.lang.CharSequence interface og tjene som tekstkilder).
  • MønsterSyntaksundtagelse objekter beskriver ulovlige regex-mønstre.

Java understøtter også mønstermatchning via forskellige metoder i sin java.lang.Streng klasse. For eksempel, boolske kampe (String regex) returnerer sandt kun hvis den påkaldte streng matcher nøjagtigt regex's regex.

Bekvemmelighedsmetoder

Bag scenen, Tændstikker() og SnorAndre regex-orienterede bekvemmelighedsmetoder implementeres i form af Regex API.

RegexDemo

Jeg har oprettet RegexDemo applikation til at demonstrere Java's regulære udtryk og de forskellige metoder, der findes i Mønster, Matcherog MønsterSyntaksundtagelse klasser. Her er kildekoden til demoen:

Fortegnelse 1. Demonstrere regexer

importere java.util.regex.Matcher; importere java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; public class RegexDemo {public static void main (String [] args) {if (args.length! = 2) {System.err.println ("brug: java RegexDemo regex input"); Vend tilbage; } // Konverter nye linjesekvenser (\ n) til nye linjetegn. args [1] = args [1] .replaceAll ("\ n", "\ n"); prøv {System.out.println ("regex =" + args [0]); System.out.println ("input =" + args [1]); Mønster p = Mønster.kompil (args [0]); Matcher m = s. Matcher (args [1]); mens (m.find ()) System.out.println ("Fundet [" + m.gruppe () + "] startende ved" + m.start () + "og slutter ved" + (m.end () - 1)); } fange (PatternSyntaxException pse) {System.err.println ("Dårlig regex:" + pse.getMessage ()); System.err.println ("Beskrivelse:" + pse.getDescription ()); System.err.println ("Indeks:" + pse.getIndex ()); System.err.println ("Forkert mønster:" + pse.getPattern ()); }}}

Den første ting RegexDemo's hoved () metode gør er at validere kommandolinjen. Dette kræver to argumenter: det første argument er en regex, og det andet argument er inputtekst, der skal matches med regex.

Du kan måske angive en ny linje (\ n) tegn som en del af inputteksten. Den eneste måde at opnå dette på er at angive en \ karakter efterfulgt af en n Karakter. hoved () konverterer denne tegnsekvens til Unicode-værdi 10.

Størstedelen af RegexDemo's kode er placeret i prøve-fangst konstruere. Det prøve blok først udgange den angivne regex og inputtekst og opretter derefter en Mønster objekt, der gemmer den kompilerede regex. (Regexes er kompileret for at forbedre ydeevnen under mønstermatchning.) En matcher ekstraheres fra Mønster objekt og bruges til gentagne gange at søge efter kampe, indtil ingen er tilbage. Det fangst blok påberåber sig forskellige MønsterSyntaksundtagelse metoder til at udtrække nyttige oplysninger om undtagelsen. Disse oplysninger udsendes derefter.

Du behøver ikke at vide mere om kildekodens funktion på dette tidspunkt; det bliver klart, når du udforsker API'en i del 2. Du skal dog kompilere liste 1. Grib koden fra liste 1, og skriv derefter følgende i din kommandolinje for at kompilere RegexDemo:

javac RegexDemo.java

Mønster og dets konstruktioner

Mønster, den første af tre klasser, der omfatter Regex API, er en samlet gengivelse af et regulært udtryk. MønsterSDK-dokumentation beskriver forskellige regex-konstruktioner, men medmindre du allerede er en ivrig regex-bruger, kan du blive forvirret af dele af dokumentationen. Hvad er kvantifikatorer og hvad er forskellen mellem grådig, modstræbendeog besiddende kvantificeringsmidler? Hvad er karakter klasser, grænse matchere, tilbage referencerog indlejrede flagudtryk? Jeg besvarer disse spørgsmål og mere i de næste afsnit.

Bogstavelige strenge

Den enkleste regex-konstruktion er den bogstavelige streng. En del af inputteksten skal matche denne konstruktions mønster for at få en vellykket mønstermatch. Overvej følgende eksempel:

java RegexDemo æble-applet

Dette eksempel forsøger at finde ud af, om der er et match for æble mønster i applet inputtekst. Følgende output afslører matchet:

regex = apple input = applet Fundet [apple] startende ved 0 og slutter ved 4

Outputtet viser os regex og inputtekst og angiver derefter en vellykket matchning af æble inden for applet. Derudover præsenterer det start- og slutningsindeks for det match: 0 og 4, henholdsvis. Startindekset identificerer den første tekstplacering, hvor der opstår et mønstermatch; slutningsindekset identificerer den sidste tekstplacering for kampen.

Antag nu, at vi angiver følgende kommandolinje:

java RegexDemo æble crabapple

Denne gang får vi følgende match med forskellige start- og slutindekser:

regex = apple input = crabapple Fundet [apple] starter ved 4 og slutter ved 8

Det omvendte scenario, hvor applet er regex og æble er inputteksten, afslører ingen match. Hele regexen skal matche, og i dette tilfælde indeholder inputteksten ikke a t efter æble.

Metategn

Mere kraftfulde regex-konstruktioner kombinerer bogstavelige tegn med metategn. For eksempel i a.b, perioden metakarakter (.) repræsenterer ethvert tegn, der vises mellem -en og b. Overvej følgende eksempel:

java RegexDemo .ox "Den hurtige brune ræv springer over den dovne okse."

Dette eksempel specificerer .okse som regex og Den hurtige brune ræv springer over den dovne okse. som inputteksten. RegexDemo søger i teksten efter matches, der begynder med et hvilket som helst tegn og slutter med okse. Det producerer følgende output:

regex = .ox input = Den hurtige brune ræv springer over den dovne okse. Fundet [ræv] starter ved 16 og slutter ved 18 Fundet [okse] starter ved 39 og slutter ved 41

Outputtet afslører to kampe: Ræv og okse (med den førende pladskarakter). Det . metacharacter matcher f i den første kamp og mellemrumstegnet i den anden kamp.

Hvad sker der, når vi udskifter .okse med perioden metakarakter? Det vil sige, hvilket output er resultatet af at angive følgende kommandolinje:

java RegexDemo. "Den hurtige brune ræv springer over den dovne okse."

Fordi metakarakteren for perioden matcher ethvert tegn, RegexDemo udsender et match for hvert tegn (inklusive afslutningstidstegnet) i inputteksten:

regex =. input = Den hurtige brune ræv springer over den dovne okse. Fundet [T] starter ved 0 og slutter ved 0 Fundet [h] starter ved 1 og slutter ved 1 Fundet [e] starter ved 2 og slutter ved 2 Fundet [] starter ved 3 og slutter ved 3 Fundet [q] starter ved 4 og slutter ved 4 Fundet [u] starter ved 5 og slutter ved 5 Fundet [i] starter ved 6 og slutter ved 6 Fundet [c] starter ved 7 og slutter ved 7 Fundet [k] starter ved 8 og slutter ved 8 Fundet [ ] starter ved 9 og slutter ved 9 Fundet [b] starter ved 10 og slutter ved 10 Fundet [r] starter ved 11 og slutter ved 11 Fundet [o] starter ved 12 og slutter ved 12 Fundet [w] starter ved 13 og slutter ved 13 Fundet [n] starter ved 14 og slutter ved 14 Fundet [] starter ved 15 og slutter ved 15 Fundet [f] starter ved 16 og slutter ved 16 Fundet [o] starter ved 17 og slutter ved 17 Fundet [x] starter ved 18 og slutter ved 18 Fundet [] starter ved 19 og slutter ved 19 Fundet [j] starter ved 20 og slutter ved 20 Fundet [u] starter ved 21 og slutter ved 21 Fundet [m] starter ved 22 og slutter ved 22 Fundet [p] starter ved 23 og slutter ved 23 Fundet [s] st kunst ved 24 og slutter ved 24 Fundet [] starter ved 25 og slutter ved 25 Fundet [o] starter ved 26 og slutter ved 26 Fundet [v] starter ved 27 og slutter ved 27 Fundet [e] starter ved 28 og slutter ved 28 Fundet [r] starter ved 29 og slutter ved 29 Fundet [] starter ved 30 og slutter ved 30 Fundet [t] starter ved 31 og slutter ved 31 Fundet [h] starter ved 32 og slutter ved 32 Fundet [e] starter ved 33 og slutter ved 33 Fundet [] starter ved 34 og slutter ved 34 Fundet [l] starter ved 35 og slutter ved 35 Fundet [a] starter ved 36 og slutter ved 36 Fundet [z] starter ved 37 og slutter ved 37 Fundet [y ] starter ved 38 og slutter ved 38 Fundet [] starter ved 39 og slutter ved 39 Fundet [o] starter ved 40 og slutter ved 40 Fundet [x] starter ved 41 og slutter ved 41 Fundet [.] starter ved 42 og slutter ved 42

Citering af metategn

At specificere . eller en hvilken som helst metakarakter som en bogstavelig karakter i en regex-konstruktion, citer metakarakteren på en af ​​følgende måder:

  • Foran metakarakteren med en tilbageslagskarakter.
  • Placer metakarakteren imellem \ Q og \ E (f.eks., \ Q. \ E).

Husk at fordoble hvert tilbageslagstegn (som i \\. eller \ Q. \ E) der vises i en streng bogstavelig som f.eks String regex = "\.";. Dobbelt ikke tilbageslagstegnet, når det vises som en del af et kommandolinjeargument.

Karakterklasser

Vi er undertiden nødt til at begrænse tegn, der producerer matches til et specifikt tegnsæt. For eksempel søger vi muligvis tekst efter vokaler -en, e, jeg, oog u, hvor enhver forekomst af en vokal indikerer en match. EN karakter klasse identificerer et sæt tegn mellem metategn med firkantet parentes ([ ]), der hjælper os med at udføre denne opgave. Mønster understøtter enkle, negation, rækkevidde, union, skæringspunkt og subtraktion karakterklasser. Vi ser på alle disse nedenfor.

Enkel karakterklasse

Det enkel karakter klasse består af tegn placeret side om side og matcher kun disse tegn. For eksempel, [abc] matcher tegn -en, bog c.

Overvej følgende eksempel:

java RegexDemo [csw] hule

Dette eksempel stemmer kun overens c med sin modstykke i hule, som vist i følgende output:

regex = [csw] input = hule Fundet [c] starter ved 0 og slutter ved 0

Negation karakter klasse

Det negation karakter klasse begynder med ^ metakarakter og matcher kun de tegn, der ikke findes i den klasse. For eksempel, [^ abc] matcher alle tegn undtagen -en, bog c.

Overvej dette eksempel:

java RegexDemo "[^ csw]" hule

Bemærk, at de dobbelte citater er nødvendige på min Windows-platform, hvis skal behandler ^ karakter som en flugtkarakter.

Dette eksempel stemmer overens -en, vog e med deres kolleger i hulesom vist her:

regex = [^ csw] input = hule Fundet [a] starter ved 1 og slutter ved 1 Fundet [v] starter ved 2 og slutter ved 2 Fundet [e] starter ved 3 og slutter ved 3

Område karakter klasse

Det række karakter klasse består af to tegn adskilt af en bindestregs metategn (-). Alle tegn, der begynder med tegnet til venstre for bindestreg og slutter med tegnet til højre for bindestreg, hører til området. For eksempel, [a-z] matcher alle alfabetiske små bogstaver. Det svarer til at specificere [abcdefghijklmnopqrstuvwxyz].

Overvej følgende eksempel:

java RegexDemo [a-c] klovn

Dette eksempel stemmer kun overens c med sin modstykke i klovn, som vist:

regex = [a-c] input = klovn Fundet [c] startende ved 0 og slutter ved 0

Fletning af flere intervaller

Du kan flette flere områder i den samme række karakterklasse ved at placere dem side om side. For eksempel, [a-zA-Z] matcher alle alfabetiske tegn med små og store bogstaver.

Union karakter klasse

Det fagforenings karakterklasse består af flere indlejrede karakterklasser og matcher alle tegn, der hører til den resulterende union. For eksempel, [a-d [m-p]] matcher tegn -en igennem d og m igennem s.

Overvej følgende eksempel:

java RegexDemo [ab [c-e]] abcdef

Dette eksempel stemmer overens -en, b, c, dog e med deres kolleger i abcdef:

regex = [ab [ce]] input = abcdef Fundet [a] starter ved 0 og slutter ved 0 Fundet [b] starter ved 1 og slutter ved 1 Fundet [c] starter ved 2 og slutter ved 2 Fundet [d] starter ved 3 og slutter ved 3 Fundet [e] starter ved 4 og slutter ved 4

Skæringspunktets karakterklasse

Det krydset karakter klasse består af tegn, der er fælles for alle indlejrede klasser, og matcher kun almindelige tegn. For eksempel, [a-z && [d-f]] matcher tegn d, eog f.

Overvej følgende eksempel:

java RegexDemo "[aeiouy && [y]]" fest

Bemærk, at de dobbelte citater er nødvendige på min Windows-platform, hvis skal behandler & tegn som kommandoseparator.

Dette eksempel stemmer kun overens y med sin modstykke i parti:

regex = [aeiouy && [y]] input = party Fundet [y] starter ved 4 og slutter ved 4