En guide til Java Regular Expressions API

1. Oversikt

I denne artikkelen vil vi diskutere Java Regex API og hvordan vanlige uttrykk kan brukes i Java-programmeringsspråk.

I verden av vanlige uttrykk er det mange forskjellige smaker å velge mellom, som grep, Perl, Python, PHP, awk og mye mer.

Dette betyr at et vanlig uttrykk som fungerer på ett programmeringsspråk, kanskje ikke fungerer i et annet. Syntaksen for vanlig uttrykk i Java ligner mest på den som finnes i Perl.

2. Oppsett

For å bruke vanlige uttrykk i Java trenger vi ikke noe spesielt oppsett. JDK inneholder en spesiell pakke java.util.regex helt dedikert til regex-operasjoner. Vi trenger bare å importere den til koden vår.

Videre, den java.lang.Streng class har også innebygd regex-støtte som vi ofte bruker i koden vår.

3. Java Regex-pakke

De java.util.regex pakken består av tre klasser: Mønster, Matcher og Mønster Syntaks Unntak:

  • Mønster objektet er en samlet regeks. De Mønster klasse gir ingen offentlige konstruktører. For å lage et mønster, må vi først påberope en av dens offentlige statiske kompilere metoder, som deretter vil returnere a Mønster gjenstand. Disse metodene godtar et regulært uttrykk som det første argumentet.
  • Matcher objektet tolker mønsteret og utfører matchoperasjoner mot en inngang String. Det definerer også ingen offentlige konstruktører. Vi får en Matcher objektet ved å påkalle matcher metode på en Mønster gjenstand.
  • MønsterSyntakseksepsjon objekt er et ukontrollert unntak som indikerer en syntaksfeil i et regulært uttrykksmønster.

Vi vil utforske disse klassene i detalj; Vi må imidlertid først forstå hvordan en regex er konstruert i Java.

Hvis du allerede er kjent med regex fra et annet miljø, kan du finne visse forskjeller, men de er minimale.

4. Enkelt eksempel

La oss starte med den enkleste brukssaken for en regex. Som vi nevnte tidligere, når en regex blir brukt på en streng, kan den matche null eller flere ganger.

Den mest grunnleggende formen for mønstermatching støttet av java.util.regex API er kamp av en String bokstavelig. For eksempel hvis det regulære uttrykket er foo og innspill String er foo, vil kampen lykkes fordi Strenger er identiske:

@Test offentlig ugyldighet givenText_whenSimpleRegexMatches_thenCorrect () {Mønster mønster = Mønster.kompil ("foo"); Matcher matcher = mønster.matcher ("foo"); assertTrue (matcher.find ()); }

Vi oppretter først en Mønster objektet ved å kalle det statiske kompilere metode og gi den et mønster vi ønsker å bruke.

Så lager vi en Matcher objektet kaller Mønster objektets matcher metode og sende den teksten vi vil se etter treff.

Etter det kaller vi metoden finne i Matcher-objektet.

De finne metoden fortsetter å gå gjennom inngangsteksten og returnerer sant for hver kamp, ​​slik at vi også kan bruke den til å finne kampantallet:

@Test offentlig ugyldighet givenText_whenSimpleRegexMatchesTwice_thenCorrect () {Mønster mønster = Mønster.kompilere ("foo"); Matcher matcher = mønster.matcher ("foofoo"); int treff = 0; while (matcher.find ()) {matches ++; } assertEquals (matches, 2); }

Siden vi kjører flere tester, kan vi trekke ut logikken for å finne antall treff i en metode som kalles runTest:

offentlig statisk int runTest (String regex, String text) {Mønster mønster = Mønster.kompil (regex); Matcher matcher = mønster. Matcher (tekst); int treff = 0; while (matcher.find ()) {matches ++; } retur kamper; }

Når vi får 0 kamper, skal testen mislykkes, ellers skal den bestå.

5. Metategn

Metategn påvirker måten et mønster matches på, og på en måte legger logikk til søkemønsteret. Java API støtter flere metategn, det enkleste er prikken “.” som samsvarer med et hvilket som helst tegn:

@Test offentlig ugyldig givenText_whenMatchesWithDotMetach_thenCorrect () {int matches = runTest (".", "Foo"); assertTrue (treff> 0); }

Tatt i betraktning det forrige eksemplet der regex foo matchet teksten foo i tillegg til foofoo to ganger. Hvis vi brukte punktmetakarakteren i regex, ville vi ikke få to treff i det andre tilfellet:

@Test offentlig ugyldig gittRepeatedText_whenMatchesOnceWithDotMetach_thenCorrect () {int matches = runTest ("foo.", "Foofoo"); assertEquals (fyrstikker, 1); }

Legg merke til prikken etter foo i regex. Matcheren samsvarer med hver tekst som er foran foo siden den siste prikkdelen betyr noe tegn etter. Så etter å ha funnet den første foo, resten blir sett på som en hvilken som helst karakter. Derfor er det bare en enkelt kamp.

API-et støtter flere andre metategn som vi vil se nærmere på i denne artikkelen.

6. Karakterklasser

Bla gjennom tjenestemannen Mønster klassespesifikasjon, vil vi oppdage sammendrag av støttede regex-konstruksjoner. Under karakterklasser har vi omtrent 6 konstruksjoner.

6.1. ELLER Klasse

Konstruert som [abc]. Noen av elementene i settet samsvarer:

@Test offentlig ugyldig gittORSet_whenMatchesAny_thenCorrect () {int matches = runTest ("[abc]", "b"); assertEquals (fyrstikker, 1); }

Hvis de alle vises i teksten, matches hver for seg uten hensyn til rekkefølge:

@Test offentlig ugyldig gittORSet_whenMatchesAnyAndAll_thenCorrect () {int matches = runTest ("[abc]", "cab"); assertEquals (fyrstikker, 3); }

De kan også alterneres som en del av en String. I det følgende eksemplet, når vi oppretter forskjellige ord ved å alternere den første bokstaven med hvert element i settet, samsvarer de alle:

@Test offentlig ugyldig gittORSet_whenMatchesAllCombinations_thenCorrect () {int matches = runTest ("[bcr] at", "bat cat rat"); assertEquals (fyrstikker, 3); }

6.2. HELLER IKKE Klasse

Ovennevnte sett negeres ved å legge til en vakt som det første elementet:

@Test offentlig ugyldighet gittNORSet_whenMatchesNon_thenCorrect () {int samsvarer = runTest ("[^ abc]", "g"); assertTrue (treff> 0); }

En annen sak:

@Test offentlig ugyldig gittNORSet_whenMatchesAllExceptElements_thenCorrect () {int matches = runTest ("[^ bcr] at", "sat mat eat"); assertTrue (treff> 0); }

6.3. Range Class

Vi kan definere en klasse som spesifiserer et område der den samsvarende teksten skal falle innenfor ved hjelp av bindestrek (-), og vi kan også negere et område.

Matchende store bokstaver:

@Test offentlig ugyldig givenUpperCaseRange_whenMatchesUpperCase_ thenCorrect () {int treff = runTest ("[A-Z]", "To store bokstaver 34 totalt"); assertEquals (fyrstikker, 2); }

Matchende små bokstaver:

@Test offentlig ugyldighet gittLowerCaseRange_whenMatchesLowerCase_ thenCorrect () {int treff = runTest ("[a-z]", "To store bokstaver 34 totalt"); assertEquals (fyrstikker, 26); }

Matcher både store og små bokstaver:

@Test offentlig ugyldig givenBothLowerAndUpperCaseRange_ whenMatchesAllLetters_thenCorrect () {int matches = runTest ("[a-zA-Z]", "To store bokstaver 34 totalt"); assertEquals (fyrstikker, 28); }

Matcher et gitt tallområde:

@Test offentlig ugyldig givenNumberRange_whenMatchesAccurately_ thenCorrect () {int matches = runTest ("[1-5]", "To store bokstaver 34 totalt"); assertEquals (fyrstikker, 2); }

Matcher et annet tallområde:

@Test offentlig ugyldig givenNumberRange_whenMatchesAccurately_ thenCorrect2 () {int matches = runTest ("[30-35]", "To store bokstaver 34 totalt"); assertEquals (fyrstikker, 1); }

6.4. Union klasse

En fagforeningsklasseklasse er et resultat av å kombinere to eller flere karakterklasser:

@Test offentlig ugyldighet givenTwoSets_whenMatchesUnion_thenCorrect () {int matches = runTest ("[1-3 [7-9]]", "123456789"); assertEquals (fyrstikker, 6); }

Ovennevnte test vil bare matche 6 av de 9 heltallene fordi unionssettet hopper over 4, 5 og 6.

6.5. Kryssklasse

I likhet med fagklassen, er denne klassen resultatet av å velge vanlige elementer mellom to eller flere sett. For å bruke kryss bruker vi &&:

@Test offentlig ugyldighet givenTwoSets_whenMatchesIntersection_thenCorrect () {int matches = runTest ("[1-6 && [3-9]]", "123456789"); assertEquals (fyrstikker, 4); }

Vi får 4 kamper fordi skjæringspunktet mellom de to settene bare har 4 elementer.

6.6. Subtraksjonsklasse

Vi kan bruke subtraksjon til å oppheve en eller flere tegnklasser, for eksempel å matche et sett med odde desimaltall:

@Test offentlig ugyldig givenSetWithSubtraction_whenMatchesAccurately_thenCorrect () {int matches = runTest ("[0-9 && [^ 2468]]", "123456789"); assertEquals (fyrstikker, 5); }

Kun 1,3,5,7,9 vil bli matchet.

7. Forhåndsdefinerte karakterklasser

Java regex API godtar også forhåndsdefinerte tegnklasser. Noen av de ovennevnte karakterklassene kan uttrykkes i kortere form, men gjør koden mindre intuitiv. Et spesielt aspekt av Java-versjonen av denne regexen er fluktkarakteren.

Som vi vil se, vil de fleste tegn starte med et tilbakeslag, som har en spesiell betydning i Java. For at disse skal bli samlet av Mønster klasse - det ledende tilbakeslaget må unngås, dvs. \ d blir til \ d.

Matchende sifre, tilsvarer [0-9]:

@Test offentlig ugyldig givenDigits_whenMatches_thenCorrect () {int matches = runTest ("\ d", "123"); assertEquals (fyrstikker, 3); }

Matchende ikke-sifre, tilsvarer [^0-9]:

@Test offentlig ugyldig givenNonDigits_whenMatches_thenCorrect () {int mathces = runTest ("\ D", "a6c"); assertEquals (fyrstikker, 2); }

Matchende hvite mellomrom:

@Test offentlig ugyldighet givenWhiteSpace_whenMatches_thenCorrect () {int matches = runTest ("\ s", "a c"); assertEquals (fyrstikker, 1); }

Matchende ikke-hvitt mellomrom:

@Test offentlig ugyldig givenNonWhiteSpace_whenMatches_thenCorrect () {int matches = runTest ("\ S", "a c"); assertEquals (fyrstikker, 2); }

Matcher et ordkarakter, tilsvarende [a-zA-Z_0-9]:

@Test offentlig ugyldig givenWordCharacter_whenMatches_thenCorrect () {int matches = runTest ("\ w", "hei!"); assertEquals (fyrstikker, 2); }

Matcher et ikke-ordtegn:

@Test offentlig ugyldighet gittNonWordCharacter_whenMatches_thenCorrect () {int matches = runTest ("\ W", "hei!"); assertEquals (fyrstikker, 1); }

8. Kvantifiserere

Java regex API lar oss også bruke kvantifiserere. Disse gjør det mulig for oss å tilpasse kampens oppførsel ytterligere ved å spesifisere antall hendelser vi skal matche mot.

For å matche en tekst null eller en gang bruker vi ? kvantifier:

@Test offentlig ugyldig givenZeroOrOneQuantifier_whenMatches_thenCorrect () {int matches = runTest ("\ a?", "Hei"); assertEquals (fyrstikker, 3); }

Alternativt kan vi bruke avstivningssyntaks, også støttet av Java regex API:

@Test offentlig ugyldig givenZeroOrOneQuantifier_whenMatches_thenCorrect2 () {int matches = runTest ("\ a {0,1}", "hei"); assertEquals (fyrstikker, 3); }

Dette eksemplet introduserer konseptet med null-lengde kamper. Det skjer slik at hvis en kvantifierers terskel for samsvar er null, samsvarer den alltid med alt i teksten, inkludert en tom String på slutten av hver innspill. Dette betyr at selv om inngangen er tom, vil den returnere en nul-lengdekamp.

Dette forklarer hvorfor vi får 3 kamper i eksemplet ovenfor til tross for at vi har en String av lengde to. Den tredje kampen er tom String.

For å matche en tekst null eller ubegrensede tider, vi oss * kvantifiserende, det er akkurat som?:

@Test offentlig ugyldig givenZeroOrManyQuantifier_whenMatches_thenCorrect () {int matches = runTest ("\ a *", "hi"); assertEquals (fyrstikker, 3); }

Støttet alternativ:

@Test offentlig ugyldig gittZeroOrManyQuantifier_whenMatches_thenCorrect2 () {int matches = runTest ("\ a {0,}", "hei"); assertEquals (fyrstikker, 3); }

Kvantifisereren med forskjell er +, den har en matchende terskel på 1. Hvis det er nødvendig String ikke forekommer i det hele tatt, vil det ikke være samsvar, ikke engang null-lengde String:

@Test offentlig ugyldig givenOneOrManyQuantifier_whenMatches_thenCorrect () {int matches = runTest ("\ a +", "hei"); assertFalse (fyrstikker); }

Støttet alternativ:

@Test offentlig ugyldig givenOneOrManyQuantifier_whenMatches_thenCorrect2 () {int matches = runTest ("\ a {1,}", "hei"); assertFalse (fyrstikker); }

Som det er i Perl og andre språk, kan avstivningssyntaxen brukes til å matche en gitt tekst flere ganger:

@Test offentlig ugyldig givenBraceQuantifier_whenMatches_thenCorrect () {int matches = runTest ("a {3}", "aaaaaa"); assertEquals (fyrstikker, 2); }

I eksemplet ovenfor får vi to kamper siden en kamp bare skjer hvis en vises tre ganger på rad. I neste test får vi imidlertid ikke en kamp siden teksten bare vises to ganger på rad:

@Test offentlig ugyldig givenBraceQuantifier_whenFailsToMatch_thenCorrect () {int matches = runTest ("a {3}", "aa"); assertFalse (treff> 0); }

Når vi bruker et område i bøylen, vil kampen være grådig, og den samsvarer fra den øvre enden av området:

@Test offentlig ugyldig givenBraceQuantifierWithRange_whenMatches_thenCorrect () {int matches = runTest ("a {2,3}", "aaaa"); assertEquals (fyrstikker, 1); }

Vi har spesifisert minst to forekomster, men ikke over tre, så vi får en enkelt kamp i stedet der matcheren ser en enkelt aaa og en lone a som ikke kan matches.

API-en lar oss imidlertid spesifisere en lat eller motvillig tilnærming slik at matcheren kan starte fra den nedre enden av området, i hvilket tilfelle samsvarende med to forekomster som aa og aa:

@Test offentlig ugyldig givenBraceQuantifierWithRange_whenMatchesLazily_thenCorrect () {int matches = runTest ("a {2,3}?", "Aaaa"); assertEquals (fyrstikker, 2); }

9. Fange grupper

API-en lar oss også behandle flere tegn som en enhet gjennom å fange grupper.

Det vil feste numre til fangegruppene og tillate henvisning med disse tallene.

I denne delen vil vi se noen eksempler på hvordan du bruker fangstgrupper i Java regex API.

La oss bruke en fangegruppe som bare samsvarer når en inngangstekst inneholder to sifre ved siden av hverandre:

@Test offentlig ugyldig givenCapturingGroup_whenMatches_thenCorrect () {int maches = runTest ("(\ d \ d)", "12"); assertEquals (fyrstikker, 1); }

Nummeret som er knyttet til kampen ovenfor er 1, ved å bruke en bakreferanse for å fortelle matcheren at vi vil matche en annen forekomst av den samsvarende delen av teksten. På denne måten, i stedet for:

@Test offentlig ugyldig givenCapturingGroup_whenMatches_thenCorrect2 () {int matches = runTest ("(\ d \ d)", "1212"); assertEquals (fyrstikker, 2); }

Der det er to separate treff for inngangen, kan vi ha en kamp, ​​men forplante den samme regex-kampen for å strekke seg over hele lengden av inngangen ved hjelp av referanse:

@Test offentlig ugyldig givenCapturingGroup_whenMatchesWithBackReference_ thenCorrect () {int matches = runTest ("(\ d \ d) \ 1", "1212"); assertEquals (fyrstikker, 1); }

Hvor vi måtte gjenta regexen uten å referere for å oppnå samme resultat:

@Test offentlig ugyldig givenCapturingGroup_whenMatches_thenCorrect3 () {int matches = runTest ("(\ d \ d) (\ d \ d)", "1212"); assertEquals (fyrstikker, 1); }

Tilsvarende, for ethvert annet antall repetisjoner, kan henvisning fra baksiden få matcheren til å se inngangen som en enkelt kamp:

@Test offentlig ugyldig gittCapturingGroup_whenMatchesWithBackReference_ thenCorrect2 () {int matches = runTest ("(\ d \ d) \ 1 \ 1 \ 1", "12121212"); assertEquals (fyrstikker, 1); }

Men hvis du endrer det siste sifferet, vil kampen mislykkes:

@Test offentlig ugyldighet givenCapturingGroupAndWrongInput_ whenMatchFailsWithBackReference_thenCorrect () {int matches = runTest ("(\ d \ d) \ 1", "1213"); assertFalse (treff> 0); }

Det er viktig å ikke glemme backslashene, dette er avgjørende i Java-syntaksen.

10. Boundary Matchers

Java regex API støtter også grensematching. Hvis vi bryr oss om hvor nøyaktig i inngangsteksten kampen skal forekomme, så er det dette vi leter etter. Med de forrige eksemplene var alt vi brydde oss om om det ble funnet en kamp eller ikke.

For å matche bare når den nødvendige regexen er sant i begynnelsen av teksten, bruker vi salget ^.

Denne testen mislykkes siden teksten hund finner du i begynnelsen:

@Test offentlig ugyldig givenText_whenMatchesAtBeginning_thenCorrect () {int matches = runTest ("^ dog", "dogs are friendly"); assertTrue (treff> 0); }

Følgende test mislykkes:

@Test offentlig ugyldig givenTextAndWrongInput_whMatchFailsAtBeginning_ thenCorrect () {int matches = runTest ("^ dog", "are dogs are friendly?"); assertFalse (treff> 0); }

For å matche bare når den nødvendige regexen er sant på slutten av teksten, bruker vi dollartegnet $. En kamp vil bli funnet i følgende tilfelle:

@Test offentlig ugyldig givenText_whenMatchesAtEnd_thenCorrect () {int matches = runTest ("dog $", "Man's best friend is a dog"); assertTrue (treff> 0); }

Og ingen treff vil bli funnet her:

@Test offentlig ugyldighet givenTextAndWrongInput_whMatchFailsAtEnd_thenCorrect () {int matches = runTest ("dog $", "is a dog mans best friend?"); assertFalse (treff> 0); }

Hvis vi bare vil ha et samsvar når den nødvendige teksten er funnet ved en ordgrense, bruker vi \ b regex i begynnelsen og slutten av regex:

Rom er en ordgrense:

@Test offentlig ugyldig givenText_whenMatchesAtWordBoundary_thenCorrect () {int matches = runTest ("\ bdog \ b", "a dog is friendly"); assertTrue (treff> 0); }

Den tomme strengen i begynnelsen av en linje er også en ordgrense:

@Test offentlig ugyldig givenText_whenMatchesAtWordBoundary_thenCorrect2 () {int matches = runTest ("\ bdog \ b", "dog is man's best friend"); assertTrue (treff> 0); }

Disse testene består fordi begynnelsen på en String, samt mellomrom mellom en tekst og en annen, markerer en ordgrense, men følgende test viser det motsatte:

@Test offentlig ugyldig givenWrongText_whenMatchFailsAtWordBoundary_thenCorrect () {int matches = runTest ("\ bdog \ b", "snoop dogg is a rapper"); assertFalse (treff> 0); }

To-ords tegn som vises på rad markerer ikke en ordgrense, men vi kan få det til å passere ved å endre slutten av regexen for å se etter en ikke-ordgrense:

@Test public void givenText_whenMatchesAtWordAndNonBoundary_thenCorrect () {int matches = runTest ("\ bdog \ B", "snoop dogg is a rapper"); assertTrue (treff> 0); }

11. Mønsterklassemetoder

Tidligere har vi bare opprettet Mønster objekter på en grunnleggende måte. Imidlertid har denne klassen en annen variant av kompilere metode som godtar et sett med flagg ved siden av regex-argumentet som påvirker måten mønsteret blir matchet på.

Disse flaggene er ganske enkelt abstrakte heltallverdier. La oss overbelaste runTest metode i testklassen slik at den kan ta et flagg som det tredje argumentet:

offentlig statisk int runTest (String regex, String text, int flags) {mønster = Pattern.compile (regex, flags); matcher = mønster. matcher (tekst); int treff = 0; while (matcher.find ()) {matches ++; } retur kamper; }

I denne delen vil vi se på de forskjellige flaggene som støttes, og hvordan de brukes.

Mønster.CANON_EQ

Dette flagget muliggjør kanonisk ekvivalens. Når det er spesifisert, vil to tegn bli vurdert å matche hvis, og bare hvis deres fulle kanoniske nedbrytning samsvarer.

Tenk på den aksentiserte Unicode-karakteren é. Dens sammensatte kodepunkt er u00E9. Unicode har imidlertid også et eget kodepunkt for komponenttegnene e, u0065 og den akutte aksenten, u0301. I dette tilfellet sammensatt karakter u00E9 kan ikke skilles fra de to tegnsekvensen u0065 u0301.

Som standard tar ikke samsvar hensyn til kanonisk ekvivalens:

@Test offentlig ugyldig gittRegexWithoutCanonEq_whenMatchFailsOnEquivalentUnicode_thenCorrect () {int matches = runTest ("\ u00E9", "\ u0065 \ u0301"); assertFalse (treff> 0); }

Men hvis vi legger til flagget, vil testen bestå:

@Test offentlig ugyldig gittRegexWithCanonEq_whenMatchesOnEquivalentUnicode_thenCorrect () {int matches = runTest ("\ u00E9", "\ u0065 \ u0301", Pattern.CANON_EQ); assertTrue (treff> 0); }

Mønster.CASE_INSENSITIVE

Dette flagget muliggjør samsvar uansett sak. Som standard tar samsvar med hensyn til sak:

@Test offentlig ugyldig gittRegexWithDefaultMatcher_whenMatchFailsOnDifferentCases_thenCorrect () {int matches = runTest ("dog", "This is a Dog"); assertFalse (treff> 0); }

Så ved å bruke dette flagget kan vi endre standard oppførsel:

@Test offentlig ugyldig gittRegexWithCaseInsensitiveMatcher _whenMatchesOnDifferentCases_thenCorrect () {int matches = runTest ("dog", "This is a Dog", Pattern.CASE_INSENSITIVE); assertTrue (treff> 0); }

Vi kan også bruke det tilsvarende, innebygde flagguttrykket for å oppnå samme resultat:

@Test offentlig ugyldig gittRegexWithEmbeddedCaseInsensitiveMatcher _whenMatchesOnDifferentCases_thenCorrect () {int matches = runTest ("(? I) dog", "This is a Dog"); assertTrue (treff> 0); }

Mønster. KOMMENTARER

Java API lar en inkludere kommentarer ved bruk av # i regex. Dette kan hjelpe til med å dokumentere komplekse regex som kanskje ikke umiddelbart er åpenbare for en annen programmerer.

Kommentarflagget får matcheren til å ignorere hvite mellomrom eller kommentarer i regexen og bare vurdere mønsteret. I standard samsvaringsmodus mislyktes følgende test:

@Test offentlig ugyldig gittRegexWithComments_whenMatchFailsWithoutFlag_thenCorrect () {int matches = runTest ("dog $ #check for word dog at end of text", "This is a dog"); assertFalse (treff> 0); }

Dette er fordi matcheren vil se etter hele regex i inngangsteksten, inkludert mellomrom og # -tegnet. Men når vi bruker flagget, vil det ignorere de ekstra mellomromene, og hver tekst som begynner med # blir sett på som en kommentar som skal ignoreres for hver linje:

@Test offentlig ugyldig gittRegexWithComments_whenMatchesWithFlag_thenCorrect () {int matches = runTest ("dog $ #check end of text", "This is a dog", Pattern.COMMENTS); assertTrue (treff> 0); }

Det er også et alternativt innebygd flagguttrykk for dette:

@Test offentlig ugyldig gittRegexWithComments_whenMatchesWithEmbeddedFlag_thenCorrect () {int matches = runTest ("(? X) dog $ #check end of text", "This is a dog"); assertTrue (treff> 0); }

Mønster. DOTALL

Som standard når vi bruker punktet "." uttrykk i regex, samsvarer vi med alle tegn i inngangen String til vi møter en ny linjekarakter.

Ved å bruke dette flagget vil kampen også omfatte linjeterminatoren. Vi vil forstå bedre med følgende eksempler. Disse eksemplene vil være litt forskjellige. Siden vi er interessert i å hevde mot de matchede String, vil vi bruke matcher‘S gruppe metode som returnerer forrige kamp.

Først vil vi se standard oppførsel:

@Test offentlig ugyldig gittRegexWithLineTerminator_whenMatchFails_thenCorrect () {Mønster mønster = Mønster.kompilere ("(. *)"); Matcher matcher = pattern.matcher ("dette er en tekst" + System.getProperty ("line.separator") + "fortsatte på en annen linje"); matcher.find (); assertEquals ("dette er en tekst", matcher.group (1)); }

Som vi kan se, er bare den første delen av inngangen før linjeterminatoren matchet.

Nå i dotall modus, vil hele teksten inkludert linjeterminatoren bli matchet:

@Test offentlig ugyldighet gittRegexWithLineTerminator_whenMatchesWithDotall_thenCorrect () {Mønster mønster = Mønster.kompil ("(. *)", Mønster.DOTALL); Matcher matcher = pattern.matcher ("dette er en tekst" + System.getProperty ("line.separator") + "fortsatte på en annen linje"); matcher.find (); assertEquals ("dette er en tekst" + System.getProperty ("line.separator") + "fortsatte på en annen linje", matcher.group (1)); }

Vi kan også bruke et innebygd flagguttrykk for å aktivere dotall modus:

@Test offentlig ugyldig gittRegexWithLineTerminator_whenMatchesWithEmbeddedDotall _thenCorrect () {Mønster mønster = Mønster.kompil ("(? S) (. *)"); Matcher matcher = pattern.matcher ("dette er en tekst" + System.getProperty ("line.separator") + "fortsatte på en annen linje"); matcher.find (); assertEquals ("dette er en tekst" + System.getProperty ("line.separator") + "fortsatte på en annen linje", matcher.group (1)); }

Mønster. LITERAL

I denne modusen gir matcher ingen spesiell betydning til metategn, escape-tegn eller regex-syntaks. Uten dette flagget vil matcheren matche følgende regeks mot alle innganger String:

@Test offentlig ugyldig gittRegex_whenMatchesWithoutLiteralFlag_thenCorrect () {int matches = runTest ("(. *)", "Text"); assertTrue (treff> 0); }

Dette er standardoppførselen vi har sett i alle eksemplene. Imidlertid, med dette flagget, vil ingen samsvar bli funnet, siden matcheren vil lete etter (.*) i stedet for å tolke det:

@Test offentlig ugyldig gittRegex_whenMatchFailsWithLiteralFlag_thenCorrect () {int matches = runTest ("(. *)", "Text", Pattern.LITERAL); assertFalse (treff> 0); }

Nå hvis vi legger til den nødvendige strengen, vil testen bestå:

@Test offentlig ugyldig gittRegex_whenMatchesWithLiteralFlag_thenCorrect () {int matches = runTest ("(. *)", "Text (. *)", Pattern.LITERAL); assertTrue (treff> 0); }

Det er ingen innebygd flaggkarakter for aktivering av bokstavelig analyse.

Mønster

Som standard ^ og $ metategn samsvarer absolutt i henholdsvis begynnelsen og slutten av hele inngangen String. Matcheren ser bort fra linjeterminatorer:

@Test offentlig ugyldig gittRegex_whenMatchFailsWithoutMultilineFlag_thenCorrect () {int matches = runTest ("dog $", "This is a dog" + System.getProperty ("line.separator") + "dette er en rev"); assertFalse (treff> 0); }

Kampen mislykkes fordi matcheren søker etter hund på slutten av hele String men hund er til stede på slutten av den første linjen i strengen.

Imidlertid, med flagget, vil den samme testen bestå siden matcheren nå tar hensyn til linjeterminatorer. Så strengen hund er funnet like før linjen slutter, derav suksess:

@Test offentlig ugyldig gittRegex_whenMatchesWithMultilineFlag_thenCorrect () {int matches = runTest ("dog $", "This is a dog" + System.getProperty ("line.separator") + "this is a fox", Pattern.MULTILINE); assertTrue (treff> 0); }

Her er den innebygde flaggversjonen:

@Test offentlig ugyldig gittRegex_whenMatchesWithEmbeddedMultilineFlag_ thenCorrect () {int matches = runTest ("(? M) dog $", "This is a dog" + System.getProperty ("line.separator") + "dette er en rev"); assertTrue (treff> 0); }

12. Matcher-klassemetoder

I denne delen vil vi se på noen nyttige metoder for Matcher klasse. Vi vil gruppere dem i henhold til funksjonalitet for klarhet.

12.1. Indeksmetoder

Indeksmetoder gir nyttige indeksverdier som viser nøyaktig hvor samsvaret ble funnet i inngangen String . I den følgende testen vil vi bekrefte start- og sluttindeksene for kampen for hund i innspillet String :

@Test offentlig ugyldig givenMatch_whenGetsIndices_thenCorrect () {Mønster mønster = Mønster.kompil ("hund"); Matcher matcher = pattern.matcher ("Denne hunden er min"); matcher.find (); assertEquals (5, matcher.start ()); assertEquals (8, matcher.end ()); }

12.2. Studiemetoder

Studiemetoder går gjennom innspillene String og returner en boolsk som indikerer om mønsteret er funnet eller ikke. Vanlig brukt er fyrstikker og ser på metoder.

De fyrstikker og ser på metoder begge prøver å matche en inngangssekvens mot et mønster. Forskjellen er at fyrstikker krever at hele inngangssekvensen blir matchet, mens ser på gjør ikke.

Begge metodene starter ved inngangen String :

@Test offentlig ugyldig nårStudyMethodsWork_thenCorrect () {Mønster mønster = Mønster.kompil ("hund"); Matcher matcher = pattern.matcher ("hunder er vennlige"); assertTrue (matcher.lookingAt ()); assertFalse (matcher.matches ()); }

Kampmetoden vil være sann i et tilfelle som dette:

@Test offentlig ugyldig nårMatchesStudyMethodWorks_thenCorrect () {Mønster mønster = Mønster.kompilere ("hund"); Matcher matcher = mønster.matcher ("hund"); assertTrue (matcher.matches ()); }

12.3. Utskiftningsmetoder

Erstatningsmetoder er nyttige for å erstatte tekst i en inngangsstreng. De vanligste er erstatte første og erstatte alle.

De erstatte første og erstatte alle metoder erstatter teksten som samsvarer med et gitt regulært uttrykk. Som navnene indikerer, erstatte første erstatter den første forekomsten, og erstatte alle erstatter alle forekomster:

@Test offentlig ugyldig nårReferFirstWorks_thenCorrect () {Mønster mønster = Mønster.kompil ("hund"); Matcher matcher = pattern.matcher ("hunder er husdyr, hunder er vennlige"); Streng newStr = matcher.replaceFirst ("katt"); assertEquals ("katter er husdyr, hunder er vennlige", newStr); }

Erstatt alle forekomster:

@Test offentlig ugyldig nårReplaceAllWorks_thenCorrect () {Mønster mønster = Mønster.kompil ("hund"); Matcher matcher = pattern.matcher ("hunder er husdyr, hunder er vennlige"); Streng newStr = matcher.replaceAll ("katt"); assertEquals ("katter er husdyr, katter er vennlige", newStr); }

De erstatte alle metoden lar oss erstatte alle kamper med samme erstatning. Hvis vi ønsker å erstatte fyrstikker etter sak, trenger vi en teknikk for erstatning av token.

13. Konklusjon

I denne artikkelen har vi lært hvordan du bruker regulære uttrykk i Java og også utforsket de viktigste funksjonene i java.util.regex pakke.

Den fullstendige kildekoden for prosjektet, inkludert alle kodeeksemplene som brukes her, finner du i GitHub-prosjektet.