Een gids voor Java Regular Expressions API

1. Overzicht

In dit artikel bespreken we de Java Regex API en hoe reguliere expressies kunnen worden gebruikt in de programmeertaal Java.

In de wereld van reguliere expressies zijn er veel verschillende smaken om uit te kiezen, zoals grep, Perl, Python, PHP, awk en nog veel meer.

Dit betekent dat een reguliere expressie die in de ene programmeertaal werkt, mogelijk niet werkt in een andere. De syntaxis van reguliere expressies in Java lijkt het meest op die in Perl.

2. Installatie

Om reguliere expressies in Java te gebruiken, hebben we geen speciale instellingen nodig. De JDK bevat een speciaal pakket java.util.regex volledig gewijd aan regex-bewerkingen. We hoeven het alleen maar in onze code te importeren.

Bovendien is de java.lang.String class heeft ook ingebouwde regex-ondersteuning die we vaak gebruiken in onze code.

3. Java Regex-pakket

De java.util.regex pakket bestaat uit drie lessen: Patroon, Matcher en PatternSyntaxException:

  • Patroon object is een gecompileerde regex. De Patroon class biedt geen openbare constructeurs. Om een ​​patroon te maken, moeten we eerst een van zijn openbare statische gegevens aanroepen compileren methoden, die dan een Patroon voorwerp. Deze methoden accepteren een reguliere expressie als het eerste argument.
  • Matcher object interpreteert het patroon en voert vergelijkingsbewerkingen uit tegen een invoer Draad. Het definieert ook geen openbare constructeurs. We krijgen een Matcher object door het aanroepen van de matcher methode op een Patroon voorwerp.
  • PatternSyntaxException object is een ongecontroleerde uitzondering die duidt op een syntaxisfout in een reguliere-expressiepatroon.

We zullen deze lessen in detail onderzoeken; we moeten echter eerst begrijpen hoe een regex in Java wordt geconstrueerd.

Als u al bekend bent met regex uit een andere omgeving, kunt u bepaalde verschillen tegenkomen, maar deze zijn minimaal.

4. Eenvoudig voorbeeld

Laten we beginnen met de eenvoudigste use-case voor een regex. Zoals we eerder hebben opgemerkt, kan een regex die op een tekenreeks wordt toegepast, nul of meer keer overeenkomen.

De meest basale vorm van patroonaanpassing die wordt ondersteund door de java.util.regex API is de wedstrijd van een Draad letterlijk. Als de reguliere expressie bijvoorbeeld foo en de input Draad is foo, de match zal slagen omdat de Snaren zijn identiek:

@Test openbare ongeldig gegevenText_whenSimpleRegexMatches_thenCorrect () {Pattern pattern = Pattern.compile ("foo"); Matcher matcher = pattern.matcher ("foo"); assertTrue (matcher.find ()); }

We maken eerst een Patroon object door zijn static aan te roepen compileren methode en het doorgeven van een patroon dat we willen gebruiken.

Vervolgens maken we een Matcher object de Patroon voorwerpen matcher methode en het doorgeven van de tekst die we willen controleren op overeenkomsten.

Daarna noemen we de methode vind in het Matcher-object.

De vind methode blijft door de invoertekst gaan en retourneert true voor elke overeenkomst, zodat we deze ook kunnen gebruiken om het aantal overeenkomsten te vinden:

@Test openbare ongeldig gegevenText_whenSimpleRegexMatchesTwice_thenCorrect () {Pattern pattern = Pattern.compile ("foo"); Matcher matcher = pattern.matcher ("foofoo"); int overeenkomsten = 0; while (matcher.find ()) {komt overeen met ++; } assertEquals (overeenkomsten, 2); }

Omdat we meer tests zullen uitvoeren, kunnen we de logica voor het vinden van het aantal overeenkomsten abstraheren in een methode met de naam test uitvoeren:

openbare statische int runTest (String regex, String text) {Pattern pattern = Pattern.compile (regex); Matcher matcher = patroon.matcher (tekst); int overeenkomsten = 0; while (matcher.find ()) {komt overeen met ++; } terugkeerovereenkomsten; }

Als we 0 overeenkomsten krijgen, zou de test moeten mislukken, anders zou hij moeten slagen.

5. Metakarakters

Meta-tekens beïnvloeden de manier waarop een patroon wordt vergeleken, en voegen in zekere zin logica toe aan het zoekpatroon. De Java API ondersteunt verschillende metatekens, waarvan de meest eenvoudige de punt is “.” die overeenkomt met elk teken:

@Test openbare ongeldig gegevenText_whenMatchesWithDotMetach_thenCorrect () {int matches = runTest (".", "Foo"); assertTrue (overeenkomsten> 0); }

Gezien het vorige voorbeeld waar regex foo kwam overeen met de tekst foo net zoals foofoo twee maal. Als we het metateken met een punt in de regex zouden gebruiken, zouden we geen twee overeenkomsten krijgen in het tweede geval:

@Test openbare ongeldig gegevenRepeatedText_whenMatchesOnceWithDotMetach_thenCorrect () {int matches = runTest ("foo.", "Foofoo"); assertEquals (overeenkomsten, 1); }

Let op de punt na de foo in de reguliere expressie. De matcher komt overeen met elke tekst die wordt voorafgegaan door foo aangezien het laatste puntgedeelte elk teken erna betekent. Dus na het vinden van de eerste foo, de rest wordt gezien als een willekeurig personage. Daarom is er maar één match.

De API ondersteunt verschillende andere metakarakters die we verderop in dit artikel zullen bespreken.

6. Karakterklassen

Bladeren door de ambtenaar Patroon class-specificatie, zullen we samenvattingen van ondersteunde regex-constructen ontdekken. Onder karakterklassen hebben we ongeveer 6 constructies.

6.1. OF Klasse

Gebouwd als [abc]. Elk van de elementen in de set komt overeen:

@Test openbare ongeldig gegevenORSet_whenMatchesAny_thenCorrect () {int matches = runTest ("[abc]", "b"); assertEquals (overeenkomsten, 1); }

Als ze allemaal in de tekst voorkomen, wordt elk afzonderlijk gematcht zonder rekening te houden met de volgorde:

@Test openbare ongeldig gegevenORSet_whenMatchesAnyAndAll_thenCorrect () {int matches = runTest ("[abc]", "cab"); assertEquals (overeenkomsten, 3); }

Ze kunnen ook worden afgewisseld als onderdeel van een Draad. Als we in het volgende voorbeeld verschillende woorden maken door de eerste letter af te wisselen met elk element van de set, komen ze allemaal overeen:

@Test openbare ongeldig gegevenORSet_whenMatchesAllCombinations_thenCorrect () {int matches = runTest ("[bcr] at", "bat cat rat"); assertEquals (overeenkomsten, 3); }

6.2. NOCH Klasse

De bovenstaande set wordt teniet gedaan door een caret toe te voegen als het eerste element:

@Test openbare ongeldig gegevenNORSet_whenMatchesNon_thenCorrect () {int matches = runTest ("[^ abc]", "g"); assertTrue (overeenkomsten> 0); }

Een ander geval:

@Test openbare ongeldig gegevenNORSet_whenMatchesAllExceptElements_thenCorrect () {int matches = runTest ("[^ bcr] bij", "sat mat eat"); assertTrue (overeenkomsten> 0); }

6.3. Bereik Klasse

We kunnen een klasse definiëren die een bereik specificeert waarbinnen de overeenkomende tekst moet vallen met een koppelteken (-), op dezelfde manier kunnen we ook een bereik negeren.

Overeenkomende hoofdletters:

@Test openbare ongeldig gegevenUpperCaseRange_whenMatchesUpperCase_ thenCorrect () {int matches = runTest ("[A-Z]", "Twee hoofdletters 34 in totaal"); assertEquals (overeenkomsten, 2); }

Overeenkomende kleine letters:

@Test openbare ongeldig gegevenLowerCaseRange_whenMatchesLowerCase_ thenCorrect () {int matches = runTest ("[a-z]", "Twee hoofdletters 34 in totaal"); assertEquals (overeenkomsten, 26); }

Overeenkomen met zowel hoofdletters als kleine letters:

@Test openbare leegte gegevenBothLowerAndUpperCaseRange_ whenMatchesAllLetters_thenCorrect () {int matches = runTest ("[a-zA-Z]", "Twee hoofdletters 34 in totaal"); assertEquals (overeenkomsten, 28); }

Overeenkomen met een bepaald nummerbereik:

@Test public void givenNumberRange_whenMatchesAccurately_ thenCorrect () {int matches = runTest ("[1-5]", "Twee hoofdletters 34 in totaal"); assertEquals (overeenkomsten, 2); }

Overeenkomend met een ander nummerbereik:

@Test openbare ongeldig gegevenNumberRange_whenMatchesAccurately_ thenCorrect2 () {int matches = runTest ("[30-35]", "Twee hoofdletters 34 in totaal"); assertEquals (overeenkomsten, 1); }

6.4. Union Klasse

Een unie-tekenklasse is het resultaat van het combineren van twee of meer tekenklassen:

@Test openbare ongeldig gegevenTwoSets_whenMatchesUnion_thenCorrect () {int matches = runTest ("[1-3 [7-9]]", "123456789"); assertEquals (overeenkomsten, 6); }

De bovenstaande test komt alleen overeen met 6 van de 9 gehele getallen omdat de verenigingsset 4, 5 en 6 overslaat.

6.5. Kruispunt Klasse

Net als bij de union-klasse, is deze klasse het resultaat van het kiezen van gemeenschappelijke elementen tussen twee of meer sets. Om kruising toe te passen, gebruiken we de &&:

@Test openbare ongeldig gegevenTwoSets_whenMatchesIntersection_thenCorrect () {int matches = runTest ("[1-6 && [3-9]]", "123456789"); assertEquals (overeenkomsten, 4); }

We krijgen 4 overeenkomsten omdat het snijpunt van de twee sets slechts 4 elementen heeft.

6.6. Aftrekken klasse

We kunnen aftrekken gebruiken om een ​​of meer tekenklassen te negeren, bijvoorbeeld door een reeks oneven decimale getallen te matchen:

@Test openbare ongeldig gegevenSetWithSubtraction_whenMatchesAccurately_thenCorrect () {int matches = runTest ("[0-9 && [^ 2468]]", "123456789"); assertEquals (overeenkomsten, 5); }

Enkel en alleen 1,3,5,7,9 zal worden gematcht.

7. Vooraf gedefinieerde karakterklassen

De Java Regex API accepteert ook vooraf gedefinieerde tekenklassen. Sommige van de bovenstaande karakterklassen kunnen in kortere vorm worden uitgedrukt, hoewel de code minder intuïtief is. Een bijzonder aspect van de Java-versie van deze regex is het escape-teken.

Zoals we zullen zien, beginnen de meeste karakters met een backslash, wat een speciale betekenis heeft in Java. Om deze te laten samenstellen door het Patroon class - de leidende backslash moet worden ontsnapt, d.w.z. \ d wordt \ d.

Overeenkomende cijfers, gelijk aan [0-9]:

@Test openbare ongeldige gegevenDigits_whenMatches_thenCorrect () {int matches = runTest ("\ d", "123"); assertEquals (overeenkomsten, 3); }

Overeenkomende niet-cijfers, gelijk aan [^0-9]:

@Test openbare ongeldig gegevenNonDigits_whenMatches_thenCorrect () {int mathces = runTest ("\ D", "a6c"); assertEquals (overeenkomsten, 2); }

Bijpassende witruimte:

@Test openbare ongeldig gegevenWhiteSpace_whenMatches_thenCorrect () {int matches = runTest ("\ s", "a c"); assertEquals (overeenkomsten, 1); }

Overeenkomende niet-witruimte:

@Test openbare ongeldig gegevenNonWhiteSpace_whenMatches_thenCorrect () {int matches = runTest ("\ S", "a c"); assertEquals (overeenkomsten, 2); }

Overeenkomen met een woordteken, gelijk aan [a-zA-Z_0-9]:

@Test openbare ongeldig gegevenWordCharacter_whenMatches_thenCorrect () {int matches = runTest ("\ w", "hi!"); assertEquals (overeenkomsten, 2); }

Overeenkomen met een niet-woordteken:

@Test openbare ongeldig gegevenNonWordCharacter_whenMatches_thenCorrect () {int matches = runTest ("\ W", "hi!"); assertEquals (overeenkomsten, 1); }

8. Kwantificatoren

De Java regex API stelt ons ook in staat om kwantoren te gebruiken. Deze stellen ons in staat om het gedrag van de match verder aan te passen door het aantal voorvallen op te geven waarmee we matchen.

Om een ​​tekst nul of een keer te matchen, gebruiken we de ? kwantor:

@Test openbare ongeldig gegevenZeroOrOneQuantifier_whenMatches_thenCorrect () {int matches = runTest ("\ a?", "Hi"); assertEquals (overeenkomsten, 3); }

Als alternatief kunnen we de brace-syntaxis gebruiken, ook ondersteund door de Java regex API:

@Test openbare ongeldig gegevenZeroOrOneQuantifier_whenMatches_thenCorrect2 () {int matches = runTest ("\ a {0,1}", "hi"); assertEquals (overeenkomsten, 3); }

Dit voorbeeld introduceert het concept van overeenkomsten met de lengte nul. Het gebeurt zo dat als de drempelwaarde voor het matchen van een kwantor nul is, deze altijd overeenkomt met alles in de tekst, inclusief een lege tekst Draad aan het einde van elke invoer. Dit betekent dat zelfs als de invoer leeg is, deze één overeenkomst met lengte nul retourneert.

Dit verklaart waarom we in het bovenstaande voorbeeld 3 overeenkomsten krijgen ondanks dat we een S hebbentring van lengte twee. De derde overeenkomst is leeg met een lengte van nul Draad.

Om een ​​tekst nul of grenzeloze keer te matchen, gebruiken we * quantifier, het is gewoon vergelijkbaar met?:

@Test openbare ongeldig gegevenZeroOrManyQuantifier_whenMatches_thenCorrect () {int matches = runTest ("\ a *", "hi"); assertEquals (overeenkomsten, 3); }

Ondersteund alternatief:

@Test openbare ongeldig gegevenZeroOrManyQuantifier_whenMatches_thenCorrect2 () {int matches = runTest ("\ a {0,}", "hi"); assertEquals (overeenkomsten, 3); }

De kwantor met een verschil is +, het heeft een bijpassende drempel van 1. Indien nodig Draad komt helemaal niet voor, er zal geen overeenkomst zijn, zelfs geen lengte nul Draad:

@Test openbare ongeldig gegevenOneOrManyQuantifier_whenMatches_thenCorrect () {int matches = runTest ("\ a +", "hi"); assertFalse (overeenkomsten); }

Ondersteund alternatief:

@Test openbare ongeldig gegevenOneOrManyQuantifier_whenMatches_thenCorrect2 () {int matches = runTest ("\ a {1,}", "hi"); assertFalse (overeenkomsten); }

Zoals het is in Perl en andere talen, kan de accoladesyntaxis worden gebruikt om een ​​bepaalde tekst een aantal keren te matchen:

@Test openbare ongeldig gegevenBraceQuantifier_whenMatches_thenCorrect () {int matches = runTest ("a {3}", "aaaaaa"); assertEquals (overeenkomsten, 2); }

In het bovenstaande voorbeeld krijgen we twee overeenkomsten omdat een overeenkomst alleen plaatsvindt als een verschijnt drie keer achter elkaar. In de volgende test krijgen we echter geen overeenkomst, omdat de tekst slechts twee keer achter elkaar voorkomt:

@Test openbare ongeldig gegevenBraceQuantifier_whenFailsToMatch_thenCorrect () {int matches = runTest ("a {3}", "aa"); assertFalse (overeenkomsten> 0); }

Wanneer we een reeks in de brace gebruiken, zal de wedstrijd hebzuchtig zijn, passend bij het hogere einde van de reeks:

@Test openbare ongeldige gegevenBraceQuantifierWithRange_whenMatches_thenCorrect () {int matches = runTest ("a {2,3}", "aaaa"); assertEquals (overeenkomsten, 1); }

We hebben ten minste twee exemplaren gespecificeerd, maar niet meer dan drie, dus we krijgen een enkele overeenkomst waar de matcher een enkele ziet aaa en een alleen een die niet kan worden geëvenaard.

De API stelt ons echter in staat om een ​​luie of onwillige benadering te specificeren, zodat de matcher kan beginnen vanaf de onderkant van het bereik, in welk geval het matchen van twee gebeurtenissen als aa en aa:

@Test openbare ongeldig gegevenBraceQuantifierWithRange_whenMatchesLazily_thenCorrect () {int matches = runTest ("a {2,3}?", "Aaaa"); assertEquals (overeenkomsten, 2); }

9. Groepen vastleggen

De API stelt ons ook in staat om behandel meerdere karakters als een enkele eenheid door groepen vast te leggen.

Het zal nummers aan de vastleggende groepen koppelen en terugverwijzing met behulp van deze nummers mogelijk maken.

In deze sectie zullen we een paar voorbeelden zien van het gebruik van vastleggingsgroepen in Java Regex API.

Laten we een vastleggroep gebruiken die alleen overeenkomt als een invoertekst twee cijfers naast elkaar bevat:

@Test openbare ongeldigheid gegevenCapturingGroup_whenMatches_thenCorrect () {int maches = runTest ("(\ d \ d)", "12"); assertEquals (overeenkomsten, 1); }

Het nummer dat aan de bovenstaande wedstrijd is gekoppeld, is 1, met behulp van een achterverwijzing om de matcher te vertellen dat we een ander exemplaar van het overeenkomende gedeelte van de tekst willen matchen. Op deze manier in plaats van:

@Test openbare ongeldige gegevenCapturingGroup_whenMatches_thenCorrect2 () {int matches = runTest ("(\ d \ d)", "1212"); assertEquals (overeenkomsten, 2); }

Als er twee afzonderlijke overeenkomsten zijn voor de invoer, kunnen we één overeenkomst hebben, maar dezelfde regex-overeenkomst verspreiden over de gehele lengte van de invoer met behulp van terugverwijzing:

@Test openbare ongeldige gegevenCapturingGroup_whenMatchesWithBackReference_ thenCorrect () {int matches = runTest ("(\ d \ d) \ 1", "1212"); assertEquals (overeenkomsten, 1); }

Waar we de regex zouden moeten herhalen zonder terugverwijzing om hetzelfde resultaat te bereiken:

@Test openbare ongeldig gegevenCapturingGroup_whenMatches_thenCorrect3 () {int matches = runTest ("(\ d \ d) (\ d \ d)", "1212"); assertEquals (overeenkomsten, 1); }

Evenzo, voor elk ander aantal herhalingen, kan terugverwijzing ervoor zorgen dat de matcher de invoer als een enkele match ziet:

@Test openbare ongeldig gegevenCapturingGroup_whenMatchesWithBackReference_ thenCorrect2 () {int matches = runTest ("(\ d \ d) \ 1 \ 1 \ 1", "12121212"); assertEquals (overeenkomsten, 1); }

Maar als u zelfs het laatste cijfer verandert, zal de match mislukken:

@Test openbare ongeldige gegevenCapturingGroupAndWrongInput_ whenMatchFailsWithBackReference_thenCorrect () {int matches = runTest ("(\ d \ d) \ 1", "1213"); assertFalse (overeenkomsten> 0); }

Het is belangrijk om de escape backslashes niet te vergeten, dit is cruciaal in de Java-syntaxis.

10. Boundary Matchers

De Java regex-API ondersteunt ook grensovereenkomsten. Als het ons interesseert waar precies in de invoertekst de overeenkomst moet voorkomen, dan is dit wat we zoeken. Met de vorige voorbeelden was het enige waar we om gaven of er een match werd gevonden of niet.

Om alleen te matchen als de vereiste regex waar is aan het begin van de tekst, gebruiken we de caret ^.

Deze test zal mislukken omdat de text hond vind je aan het begin:

@Test openbare ongeldig gegevenText_whenMatchesAtBeginning_thenCorrect () {int matches = runTest ("^ hond", "honden zijn vriendelijk"); assertTrue (overeenkomsten> 0); }

De volgende test zal mislukken:

@Test openbare ongeldig gegevenTextAndWrongInput_whenMatchFailsAtBeginning_ thenCorrect () {int matches = runTest ("^ hond", "zijn honden vriendelijk?"); assertFalse (overeenkomsten> 0); }

Om alleen te matchen als de vereiste regex waar is aan het einde van de tekst, gebruiken we het dollarteken $. Een match wordt gevonden in het volgende geval:

@Test openbare ongeldig gegevenText_whenMatchesAtEnd_thenCorrect () {int matches = runTest ("dog $", "Man's beste vriend is een hond"); assertTrue (overeenkomsten> 0); }

En hier wordt geen match gevonden:

@Test openbare ongeldig gegevenTextAndWrongInput_whenMatchFailsAtEnd_thenCorrect () {int matches = runTest ("dog $", "is de beste vriend van een hondenman?"); assertFalse (overeenkomsten> 0); }

Als we alleen een overeenkomst willen als de vereiste tekst op een woordgrens wordt gevonden, gebruiken we \ b regex aan het begin en einde van de regex:

Spatie is een woordgrens:

@Test openbare ongeldig gegevenText_whenMatchesAtWordBoundary_thenCorrect () {int matches = runTest ("\ bdog \ b", "een hond is vriendelijk"); assertTrue (overeenkomsten> 0); }

De lege string aan het begin van een regel is ook een woordgrens:

@Test openbare ongeldig gegevenText_whenMatchesAtWordBoundary_thenCorrect2 () {int matches = runTest ("\ bdog \ b", "hond is de beste vriend van de mens"); assertTrue (overeenkomsten> 0); }

Deze tests slagen omdat het begin van een Draad, evenals de ruimte tussen de ene tekst en de andere, markeert een woordgrens, maar de volgende test laat het tegenovergestelde zien:

@Test openbare ongeldig gegevenWrongText_whenMatchFailsAtWordBoundary_thenCorrect () {int matches = runTest ("\ bdog \ b", "snoop dogg is een rapper"); assertFalse (overeenkomsten> 0); }

Tekens van twee woorden die in een rij verschijnen, markeren geen woordgrens, maar we kunnen het laten passeren door het einde van de regex te veranderen om te zoeken naar een niet-woordgrens:

@Test openbare ongeldig gegevenText_whenMatchesAtWordAndNonBoundary_thenCorrect () {int matches = runTest ("\ bdog \ B", "snoop dogg is een rapper"); assertTrue (overeenkomsten> 0); }

11. Patroonglassen

Eerder hebben we alleen gemaakt Patroon objecten op een eenvoudige manier. Deze klasse heeft echter een andere variant van de compileren methode die een set vlaggen naast het regex-argument accepteert en die de manier waarop het patroon overeenkomt, beïnvloedt.

Deze vlaggen zijn eenvoudigweg geabstraheerde gehele getallen. Laten we de test uitvoeren methode in de testklasse, zodat deze een vlag als derde argument kan hebben:

openbare statische int runTest (String regex, String text, int vlaggen) {pattern = Pattern.compile (regex, vlaggen); matcher = patroon.matcher (tekst); int overeenkomsten = 0; while (matcher.find ()) {komt overeen met ++; } terugkeerovereenkomsten; }

In deze sectie zullen we kijken naar de verschillende ondersteunde vlaggen en hoe ze worden gebruikt.

Patroon.CANON_EQ

Deze vlag maakt canonieke gelijkwaardigheid mogelijk. Indien gespecificeerd, worden twee karakters geacht overeen te komen als, en alleen als, hun volledige canonieke decomposities overeenkomen.

Overweeg het Unicode-teken met accenten é. Het samengestelde codepunt is u00E9. Unicode heeft echter ook een apart codepunt voor de samenstellende tekens e, u0065 en het accent aigu, u0301. In dit geval samengesteld karakter u00E9 is niet te onderscheiden van de reeks van twee tekens u0065 u0301.

Bij het matchen wordt standaard geen rekening gehouden met canonieke gelijkwaardigheid:

@Test openbare ongeldig gegevenRegexWithoutCanonEq_whenMatchFailsOnEquivalentUnicode_thenCorrect () {int matches = runTest ("\ u00E9", "\ u0065 \ u0301"); assertFalse (overeenkomsten> 0); }

Maar als we de vlag toevoegen, zal de test slagen:

@Test openbare ongeldig gegevenRegexWithCanonEq_whenMatchesOnEquivalentUnicode_thenCorrect () {int matches = runTest ("\ u00E9", "\ u0065 \ u0301", Pattern.CANON_EQ); assertTrue (overeenkomsten> 0); }

Pattern.CASE_INSENSITIVE

Deze vlag maakt afstemming mogelijk, ongeacht het geval. Standaard wordt bij het matchen rekening gehouden met:

@Test openbare ongeldig gegevenRegexWithDefaultMatcher_whenMatchFailsOnDifferentCases_thenCorrect () {int matches = runTest ("hond", "Dit is een hond"); assertFalse (overeenkomsten> 0); }

Dus met behulp van deze vlag kunnen we het standaardgedrag wijzigen:

@Test openbare ongeldig gegevenRegexWithCaseInsensitiveMatcher _whenMatchesOnDifferentCases_thenCorrect () {int matches = runTest ("hond", "Dit is een hond", Pattern.CASE_INSENSITIVE); assertTrue (overeenkomsten> 0); }

We kunnen ook de equivalente, ingesloten vlaguitdrukking gebruiken om hetzelfde resultaat te bereiken:

@Test openbare ongeldig gegevenRegexWithEmbeddedCaseInsensitiveMatcher _whenMatchesOnDifferentCases_thenCorrect () {int matches = runTest ("(? I) hond", "Dit is een hond"); assertTrue (overeenkomsten> 0); }

Pattern.COMMENTS

Met de Java API kan men opmerkingen opnemen met # in de regex. Dit kan helpen bij het documenteren van complexe regex die misschien niet meteen duidelijk is voor een andere programmeur.

De commentarenvlag zorgt ervoor dat de matcher elke witruimte of commentaar in de reguliere expressie negeert en alleen rekening houdt met het patroon. In de standaard matching-modus zou de volgende test mislukken:

@Test openbare ongeldig gegevenRegexWithComments_whenMatchFailsWithoutFlag_thenCorrect () {int matches = runTest ("dog $ #check voor woord hond aan het einde van de tekst", "Dit is een hond"); assertFalse (overeenkomsten> 0); }

Dit komt doordat de matcher de volledige regex in de invoertekst zoekt, inclusief de spaties en het teken #. Maar als we de vlag gebruiken, worden de extra spaties genegeerd en wordt elke tekst die begint met # gezien als een opmerking die voor elke regel moet worden genegeerd:

@Test openbare ongeldig gegevenRegexWithComments_whenMatchesWithFlag_thenCorrect () {int matches = runTest ("dog $ #check end of text", "This is a dog", Pattern.COMMENTS); assertTrue (overeenkomsten> 0); }

Hiervoor is ook een alternatieve ingesloten vlaguitdrukking:

@Test openbare ongeldig gegevenRegexWithComments_whenMatchesWithEmbeddedFlag_thenCorrect () {int matches = runTest ("(? X) dog $ #check end of text", "Dit is een hond"); assertTrue (overeenkomsten> 0); }

Patroon.DOTALL

Als we de punt "." Gebruiken, expressie in regex, we matchen elk teken in de invoer Draad totdat we een nieuw lijnteken tegenkomen.

Met behulp van deze vlag zal de match ook de line terminator bevatten. We zullen het beter begrijpen met de volgende voorbeelden. Deze voorbeelden zullen een beetje anders zijn. Omdat we geïnteresseerd zijn in het verdedigen tegen de gematchte Draad, we zullen gebruiken matcher‘S groep methode die de vorige overeenkomst retourneert.

Eerst zullen we het standaardgedrag zien:

@Test openbare leegte gegevenRegexWithLineTerminator_whenMatchFails_thenCorrect () {Patroonpatroon = Pattern.compile ("(. *)"); Matcher matcher = pattern.matcher ("dit is een tekst" + System.getProperty ("line.separator") + "vervolgd op een andere regel"); matcher.find (); assertEquals ("dit is een tekst", matcher.group (1)); }

Zoals we kunnen zien, wordt alleen het eerste deel van de invoer vóór de lijnafsluiter aangepast.

Nu in dotall modus, zal de volledige tekst inclusief de regelterminator worden aangepast:

@Test openbare leegte gegevenRegexWithLineTerminator_whenMatchesWithDotall_thenCorrect () {Patroonpatroon = Pattern.compile ("(. *)", Pattern.DOTALL); Matcher matcher = pattern.matcher ("dit is een tekst" + System.getProperty ("line.separator") + "vervolgd op een andere regel"); matcher.find (); assertEquals ("dit is een tekst" + System.getProperty ("line.separator") + "vervolgd op een andere regel", matcher.group (1)); }

We kunnen ook een ingesloten vlaguitdrukking gebruiken om dotall modus:

@Test openbare leegte gegevenRegexWithLineTerminator_whenMatchesWithEmbeddedDotall _thenCorrect () {Patroonpatroon = Pattern.compile ("(? S) (. *)"); Matcher matcher = pattern.matcher ("dit is een tekst" + System.getProperty ("line.separator") + "vervolgd op een andere regel"); matcher.find (); assertEquals ("dit is een tekst" + System.getProperty ("line.separator") + "vervolgd op een andere regel", matcher.group (1)); }

Patroon.LITERAAL

In deze modus geeft matcher geen speciale betekenis aan metatekens, escape-tekens of regex-syntaxis. Zonder deze vlag zal de matcher de volgende regex matchen met elke invoer Draad:

@Test openbare ongeldig gegevenRegex_whenMatchesWithoutLiteralFlag_thenCorrect () {int matches = runTest ("(. *)", "Text"); assertTrue (overeenkomsten> 0); }

Dit is het standaardgedrag dat we in alle voorbeelden hebben gezien. Met deze vlag zal er echter geen match worden gevonden, aangezien de matcher zal zoeken (.*) in plaats van het te interpreteren:

@Test openbare ongeldig gegevenRegex_whenMatchFailsWithLiteralFlag_thenCorrect () {int matches = runTest ("(. *)", "Text", Pattern.LITERAL); assertFalse (overeenkomsten> 0); }

Als we nu de vereiste string toevoegen, zal de test slagen:

@Test openbare ongeldig gegevenRegex_whenMatchesWithLiteralFlag_thenCorrect () {int matches = runTest ("(. *)", "Text (. *)", Pattern.LITERAL); assertTrue (overeenkomsten> 0); }

Er is geen ingebed vlagteken om letterlijke parsing in te schakelen.

Patroon MULTILINE

Standaard ^ en $ metatekens komen absoluut overeen aan respectievelijk het begin en het einde van de gehele invoer Draad. De matcher negeert eventuele regelafsluiters:

@Test openbare ongeldig gegevenRegex_whenMatchFailsWithoutMultilineFlag_thenCorrect () {int matches = runTest ("dog $", "Dit is een hond" + System.getProperty ("line.separator") + "dit is een vos"); assertFalse (overeenkomsten> 0); }

De match mislukt omdat de matcher zoekt naar hond aan het einde van de hele Draad maar de hond staat aan het einde van de eerste regel van de string.

Met de vlag zal dezelfde test echter slagen, aangezien de matcher nu rekening houdt met lijnterminators. Dus de String hond wordt gevonden net voordat de regel eindigt, vandaar succes:

@Test openbare ongeldig gegevenRegex_whenMatchesWithMultilineFlag_thenCorrect () {int matches = runTest ("dog $", "Dit is een hond" + System.getProperty ("line.separator") + "dit is een vos", Pattern.MULTILINE); assertTrue (overeenkomsten> 0); }

Hier is de ingesloten vlagversie:

@Test openbare ongeldig gegevenRegex_whenMatchesWithEmbeddedMultilineFlag_ thenCorrect () {int matches = runTest ("(? M) dog $", "Dit is een hond" + System.getProperty ("line.separator") + "dit is een vos"); assertTrue (overeenkomsten> 0); }

12. Matcher Class-methoden

In deze sectie zullen we enkele nuttige methoden van de Matcher klasse. Voor de duidelijkheid zullen we ze groeperen op functionaliteit.

12.1. Index methoden

Indexmethoden bieden nuttige indexwaarden die precies aangeven waar de overeenkomst in de invoer is gevonden Draad . In de volgende test zullen we de start- en eindindices van de wedstrijd voor bevestigen hond in de ingang Draad :

@Test openbare ongeldig gegevenMatch_whenGetsIndices_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = pattern.matcher ("Deze hond is van mij"); matcher.find (); assertEquals (5, matcher.start ()); assertEquals (8, matcher.end ()); }

12.2. Studiemethoden

Studiemethoden gaan door de input Draad en retourneer een booleaanse waarde die aangeeft of het patroon al dan niet is gevonden. Veel gebruikt zijn wedstrijden en kijken naar methoden.

De wedstrijden en kijken naar methoden proberen beide een invoersequentie af te stemmen op een patroon. Het verschil is dat wedstrijden vereist dat de volledige invoersequentie wordt aangepast, while kijken naar doet niet.

Beide methoden beginnen bij het begin van de invoer Draad :

@Test openbare leegte whenStudyMethodsWork_thenCorrect () {Patroonpatroon = Pattern.compile ("hond"); Matcher matcher = pattern.matcher ("honden zijn vriendelijk"); assertTrue (matcher.lookingAt ()); assertFalse (matcher.matches ()); }

De matches-methode retourneert true in een geval als dit:

@Test public void whenMatchesStudyMethodWorks_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = pattern.matcher ("hond"); assertTrue (matcher.matches ()); }

12.3. Vervangingsmethoden

Vervangingsmethoden zijn handig om tekst in een invoertekenreeks te vervangen. De meest voorkomende zijn vervangenFirst en vervang alles.

De vervangenFirst en vervang alles methoden vervangen de tekst die overeenkomt met een bepaalde reguliere expressie. Zoals hun namen aangeven, vervangenFirst vervangt het eerste exemplaar, en vervang alles vervangt alle instanties:

@Test openbare leegte whenReplaceFirstWorks_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = pattern.matcher ("honden zijn huisdieren, honden zijn vriendelijk"); String newStr = matcher.replaceFirst ("cat"); assertEquals ("katten zijn huisdieren, honden zijn vriendelijk", newStr); }

Vervang alle voorvallen:

@Test openbare ongeldig whenReplaceAllWorks_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = pattern.matcher ("honden zijn huisdieren, honden zijn vriendelijk"); String newStr = matcher.replaceAll ("cat"); assertEquals ("katten zijn huisdieren, katten zijn vriendelijk", newStr); }

De vervang alles methode stelt ons in staat om alle wedstrijden door dezelfde vervanging te vervangen. Als we matches per geval willen vervangen, hebben we een techniek voor het vervangen van tokens nodig.

13. Conclusie

In dit artikel hebben we geleerd hoe we reguliere expressies in Java kunnen gebruiken en hebben we ook de belangrijkste kenmerken van het java.util.regex pakket.

De volledige broncode voor het project inclusief alle codevoorbeelden die hier worden gebruikt, is te vinden in het GitHub-project.