Sprievodca rozhraním Java Regular Expressions API

1. Prehľad

V tomto článku sa budeme zaoberať rozhraním API Java Regex a tým, ako možno používať regulárne výrazy v programovacom jazyku Java.

Vo svete regulárnych výrazov je na výber veľa rôznych príchutí, ako napríklad grep, Perl, Python, PHP, awk a oveľa viac.

To znamená, že regulárny výraz, ktorý funguje v jednom programovacom jazyku, nemusí fungovať v inom. Syntax regulárneho výrazu v Jave je najviac podobná syntaxe v Perle.

2. Inštalácia

Aby sme mohli používať regulárne výrazy v Jave, nepotrebujeme žiadne špeciálne nastavenie. JDK obsahuje špeciálne balenie java.util.regex úplne venovaný operáciám regulárneho výrazu. Potrebujeme ho iba importovať do nášho kódu.

Okrem toho java.lang.String trieda má tiež zabudovanú podporu regexu, ktorú bežne používame v našom kóde.

3. Balík Java Regex

The java.util.regex balíček sa skladá z troch tried: Vzor, Matcher a PatternSyntaxException:

  • Vzor objekt je skompilovaný regulárny výraz. The Vzor trieda neposkytuje žiadnych verejných konštruktorov. Aby sme vytvorili vzor, ​​musíme najskôr vyvolať jeden z jeho verejných statických parametrov zostaviť metódy, ktoré potom vrátia a Vzor objekt. Tieto metódy akceptujú regulárny výraz ako prvý argument.
  • Matcher objekt interpretuje vzor a vykonáva operácie zhody so vstupom String. Taktiež nedefinuje žiadnych verejných konštruktérov. Získame a Matcher objekt vyvolaním dohadzovač metóda na a Vzor objekt.
  • PatternSyntaxException object je nekontrolovaná výnimka, ktorá označuje chybu syntaxe vo vzore regulárneho výrazu.

Budeme skúmať tieto triedy podrobne; najskôr však musíme pochopiť, ako je regex zostavený v Jave.

Ak už regulárne výrazy z iného prostredia poznáte, možno zistíte určité rozdiely, sú však minimálne.

4. Jednoduchý príklad

Začnime s najjednoduchším prípadom použitia pre regulárny výraz. Ako sme už uviedli skôr, keď sa na reťazec použije regulárny výraz, môže sa zhodovať s nulou alebo viackrát.

Najzákladnejšia forma porovnávania vzorov podporovaná java.util.regex API je zápas a String doslovne. Napríklad ak je regulárny výraz foo a vstup String je foo, bude zápas úspešný, pretože Struny sú identické:

@Test public void givenText_whenSimpleRegexMatches_thenCorrect () {Pattern pattern = Pattern.compile ("foo"); Matcher matcher = pattern.matcher ("foo"); assertTrue (matcher.find ()); }

Najskôr vytvoríme a Vzor objekt volaním jeho statického zostaviť metóda a jej odovzdanie vzor, ​​ktorý chceme použiť.

Potom vytvoríme a Matcher objekt volá Vzor objektu dohadzovač metóda a odovzdať jej text, ktorý chceme skontrolovať, či sa zhoduje.

Potom zavoláme metódu Nájsť v objekte Matcher.

The Nájsť metóda stále napreduje cez vstupný text a vracia sa pravdivé pre každú zhodu, takže ju môžeme použiť aj na nájdenie počtu zhôd:

@Test public void givenText_whenSimpleRegexMatchesTwice_thenCorrect () {Pattern pattern = Pattern.compile ("foo"); Matcher matcher = pattern.matcher ("foofoo"); int zápasy = 0; while (matcher.find ()) {zodpovedá ++; } assertEquals (zápasy, 2); }

Pretože spustíme viac testov, môžeme abstraktnú logiku pre nájdenie počtu zhôd v metóde s názvom runTest:

public static int runTest (Regex reťazca, text reťazca) {Pattern pattern = Pattern.compile (regex); Matcher matcher = pattern.matcher (text); int zápasy = 0; while (matcher.find ()) {zodpovedá ++; } návratové zhody; }

Keď dostaneme 0 zápasov, test by mal zlyhať, inak by mal prejsť.

5. Metaznaky

Metaznaky ovplyvňujú spôsob priraďovania vzoru, čo zvyšuje logiku vzoru vyhľadávania. Rozhranie Java API podporuje niekoľko metaznakov, z ktorých najpriamejšia je bodka “.” ktorý sa zhoduje s ľubovoľným znakom:

@Test public void givenText_whenMatchesWithDotMetach_thenCorrect () {int zápasy = runTest (".", "Foo"); assertTrue (zápasy> 0); }

Ak vezmeme do úvahy predchádzajúci príklad, kde je regulárny výraz foo zhoduje sa s textom foo ako aj foofoo dvakrát. Keby sme v regexe použili bodovú metaznak, nezískali by sme v druhom prípade dva zápasy:

@Test public void givenRepeatedText_whenMatchesOnceWithDotMetach_thenCorrect () {int zápasy = runTest ("foo.", "Foofoo"); assertEquals (zápasy, 1); }

Všimnite si bodku za foo v regulárnom výraze. Nástroj na porovnávanie sa zhoduje s každým textom, pred ktorým je znak foo pretože posledná bodková časť znamená akýkoľvek znak po. Takže po nájdení prvého foo, na zvyšok sa pozerá ako na ktorúkoľvek postavu. Preto je iba jeden zápas.

API podporuje niekoľko ďalších meta znakov ktorej sa budeme ďalej venovať v tomto článku.

6. Triedy znakov

Prechádzanie úradníkom Vzor špecifikácia triedy, objavíme súhrny podporovaných regexových konštrukcií. V triedach znakov máme asi 6 konštrukcií.

6.1. ALEBO Trieda

Konštruované ako [abc]. Zhoduje sa ktorýkoľvek z prvkov v súprave:

@Test public void givenORSet_whenMatchesAny_thenCorrect () {int zápasy = runTest ("[abc]", "b"); assertEquals (zápasy, 1); }

Ak sa všetky zobrazia v texte, každý sa priradí osobitne bez ohľadu na poradie:

@Test public void givenORSet_whenMatchesAnyAndAll_thenCorrect () {int zápasy = runTest ("[abc]", "cab"); assertEquals (zápasy, 3); }

Môžu sa tiež striedať ako súčasť a String. Keď v nasledujúcom príklade vytvoríme rôzne slová tak, že striedame prvé písmeno s každým prvkom množiny, všetky sa zhodujú:

@Test public void givenORSet_whenMatchesAllCombinations_thenCorrect () {int matches = runTest ("[bcr] at", "bat cat rat"); assertEquals (zápasy, 3); }

6.2. NOR Trieda

Vyššie uvedená množina je negovaná pridaním vsuvky ako prvého prvku:

@Test public void givenNORSet_whenMatchesNon_thenCorrect () {int zápasy = runTest ("[^ abc]", "g"); assertTrue (zápasy> 0); }

Iný prípad:

@Test public void givenNORSet_whenMatchesAllExceptElements_thenCorrect () {int matches = runTest ("[^ bcr] at", "sat mat eat"); assertTrue (zápasy> 0); }

6.3. Trieda rozsahu

Môžeme definovať triedu, ktorá určuje rozsah, do ktorého by sa zhodovaný text mal dostať, pomocou spojovníka (-), podobne môžeme tiež negovať rozsah.

Zhodné veľké písmená:

@Test public void givenUpperCaseRange_whenMatchesUpperCase_ thenCorrect () {int zápasy = runTest ("[A-Z]", "Dve veľké abecedy 34 celkovo"); assertEquals (zápasy, 2); }

Zhodné malé písmená:

@Test public void givenLowerCaseRange_whenMatchesLowerCase_ thenCorrect () {int zápasy = runTest ("[a-z]", "Dve veľké písmená celkovo 34"); assertEquals (zápasy, 26); }

Zhoda veľkých aj malých písmen:

@Test public void givenBothLowerAndUpperCaseRange_ whenMatchesAllLetters_thenCorrect () {int matches = runTest ("[a-zA-Z]", "Dve veľké písmená 34 celkovo"); assertEquals (zápasy, 28); }

Zhoda s daným rozsahom čísel:

@Test public void givenNumberRange_whenMatchesAccurately_ thenCorrect () {int zápasy = runTest ("[1-5]", "Dve veľké písmená celkovo 34"); assertEquals (zápasy, 2); }

Zhodné s iným rozsahom čísel:

@Test public void givenNumberRange_whenMatchesAccurately_ thenCorrect2 () {int zápasy = runTest ("[30-35]", "Dve veľké písmená celkovo 34"); assertEquals (zápasy, 1); }

6.4. Union Class

Trieda zjednotených znakov je výsledkom kombinácie dvoch alebo viacerých tried znakov:

@Test public void givenTwoSets_whenMatchesUnion_thenCorrect () {int zápasy = runTest ("[1-3 [7-9]]", "123456789"); assertEquals (zápasy, 6); }

Vyššie uvedený test bude zodpovedať iba 6 z 9 celých čísel, pretože spojovacia sada preskočí 4, 5 a 6.

6.5. Križovatková trieda

Podobne ako v triede union, aj táto trieda je výsledkom výberu spoločných prvkov medzi dvoma alebo viacerými množinami. Na použitie križovatky používame &&:

@Test public void givenTwoSets_whenMatchesIntersection_thenCorrect () {int zápasy = runTest ("[1-6 && [3-9]]", "123456789"); assertEquals (zápasy, 4); }

Získame 4 zápasy, pretože priesečník týchto dvoch sád má iba 4 prvky.

6.6. Trieda odčítania

Odčítaním môžeme vylúčiť jednu alebo viac tried znakov, napríklad vyhovujúcich množine nepárnych desatinných čísel:

@Test public void givenSetWithSubtraction_whenMatchesAccurately_thenCorrect () {int zápasy = runTest ("[0-9 && [^ 2468]]", "123456789"); assertEquals (zápasy, 5); }

Iba 1,3,5,7,9 bude spárované.

7. Preddefinované triedy znakov

Rozhranie Java regex API tiež prijíma preddefinované triedy znakov. Niektoré z vyššie uvedených tried znakov je možné vyjadriť v kratšej forme, aj keď je kód menej intuitívny. Jedným špeciálnym aspektom verzie Java tohto regulárneho výrazu je úniková postava.

Ako uvidíme, väčšina znakov bude začínať spätným lomítkom, ktoré má v Jave špeciálny význam. Za to, aby ich zostavil Vzor trieda - predné spätné lomítko musí byť uniknuté t.j. \ d sa stáva \ d.

Zhodné číslice, ekvivalent k [0-9]:

@Test public void givenDigits_whenMatches_thenCorrect () {int zápasy = runTest ("\ d", "123"); assertEquals (zápasy, 3); }

Zhodné iné ako číslice, ekvivalent k [^0-9]:

@Test public void givenNonDigits_whenMatches_thenCorrect () {int mathces = runTest ("\ D", "a6c"); assertEquals (zápasy, 2); }

Zodpovedajúce biele miesto:

@Test public void givenWhiteSpace_whenMatches_thenCorrect () {int zápasy = runTest ("\ s", "a c"); assertEquals (zápasy, 1); }

Zhodné iné ako biele miesto:

@Test public void givenNonWhiteSpace_whenMatches_thenCorrect () {int zápasy = runTest ("\ S", "a c"); assertEquals (zápasy, 2); }

Zhoda znaku slova, ekvivalent k [a-zA-Z_0-9]:

@Test public void givenWordCharacter_whenMatches_thenCorrect () {int zápasy = runTest ("\ w", "ahoj!"); assertEquals (zápasy, 2); }

Zhoda neslovného znaku:

@Test public void givenNonWordCharacter_whenMatches_thenCorrect () {int zápasy = runTest ("\ W", "ahoj!"); assertEquals (zápasy, 1); }

8. Kvantifikátory

Rozhranie Java regex API nám tiež umožňuje používať kvantifikátory. Tieto nám umožňujú ďalej vylepšiť správanie zápasu zadaním počtu výskytov, ktoré sa majú porovnávať.

Na priradenie nulového alebo jednorazového textu používame znak ? kvantifikátor:

@Test public void givenZeroOrOneQuantifier_whenMatches_thenCorrect () {int zápasy = runTest ("\ a?", "Ahoj"); assertEquals (zápasy, 3); }

Prípadne môžeme použiť syntax zátvorky, ktorú podporuje aj API Java regex:

@Test public void givenZeroOrOneQuantifier_whenMatches_thenCorrect2 () {int zápasy = runTest ("\ a {0,1}", "ahoj"); assertEquals (zápasy, 3); }

Tento príklad zavádza koncept zápasov s nulovou dĺžkou. Stáva sa, že ak je prah kvantifikátora na zhodu nulový, vždy sa zhoduje so všetkým v texte vrátane prázdneho String na konci každého vstupu. To znamená, že aj keď je vstup prázdny, vráti jednu zhodu nulovej dĺžky.

To vysvetľuje, prečo vo vyššie uvedenom príklade dostaneme 3 zápasy napriek tomu, že máme String dĺžky dva. Tretí zápas je prázdny String.

Ak chcete, aby text zodpovedal nule alebo neobmedzene, používame * kvantifikátor, je to podobné ako?:

@Test public void givenZeroOrManyQuantifier_whenMatches_thenCorrect () {int zápasy = runTest ("\ a *", "ahoj"); assertEquals (zápasy, 3); }

Podporovaná alternatíva:

@Test public void givenZeroOrManyQuantifier_whenMatches_thenCorrect2 () {int zápasy = runTest ("\ a {0,}", "ahoj"); assertEquals (zápasy, 3); }

Kvantifikátor s rozdielom je +, má prahovú hodnotu zhody 1. Ak je to potrebné String sa nevyskytuje vôbec, nebude existovať zhoda, dokonca ani nulová dĺžka String:

@Test public void givenOneOrManyQuantifier_whenMatches_thenCorrect () {int zápasy = runTest ("\ a +", "ahoj"); assertFalse (zápasy); }

Podporovaná alternatíva:

@Test public void givenOneOrManyQuantifier_whenMatches_thenCorrect2 () {int zápasy = runTest ("\ a {1,}", "ahoj"); assertFalse (zápasy); }

Rovnako ako v jazyku Perl a ďalších jazykoch možno syntaxu zátvorky použiť na párovanie daného textu niekoľkokrát:

@Test public void givenBraceQuantifier_whenMatches_thenCorrect () {int zápasy = runTest ("a {3}", "aaaaaa"); assertEquals (zápasy, 2); }

Vo vyššie uvedenom príklade získame dva zápasy, pretože k zápasu dôjde, iba ak a sa objaví trikrát za sebou. V nasledujúcom teste však nezískame zhodu, pretože text sa zobrazuje iba dvakrát za sebou:

@Test public void givenBraceQuantifier_whenFailsToMatch_thenCorrect () {int zápasy = runTest ("a {3}", "aa"); assertFalse (zápasy> 0); }

Keď použijeme rozsah v zátvorke, zhoda bude nenásytná a bude zodpovedať od horného konca rozsahu:

@Test public void givenBraceQuantifierWithRange_whenMatches_thenCorrect () {int zápasy = runTest ("a {2,3}", "aaaa"); assertEquals (zápasy, 1); }

Zadali sme minimálne dva výskyty, ale nepresiahli sme tri, takže namiesto toho dostaneme jednu zhodu, kde hráč vidí jediný aaa a a osamelý, ktorému sa nedá priradiť.

Rozhranie API nám však umožňuje určiť lenivý alebo neochotný prístup, takže porovnávač môže začať od dolného konca rozsahu, v takom prípade bude vyhovovať dvom výskytom ako aa a aa:

@Test public void givenBraceQuantifierWithRange_whenMatchesLazily_thenCorrect () {int zápasy = runTest ("a {2,3}?", "Aaaa"); assertEquals (zápasy, 2); }

9. Zachytávanie skupín

API nám to tiež umožňuje zaobchádzať s viacerými znakmi ako s jednou jednotkou prostredníctvom skupín snímania.

Priradí čísla zoskupujúcim skupinám a umožní spätné odkazy pomocou týchto čísel.

V tejto časti uvidíme niekoľko príkladov, ako používať skupiny zachytávania v rozhraní Java regex API.

Použime skupinu snímania, ktorá sa zhoduje, iba ak vstupný text obsahuje dve číslice vedľa seba:

@Test public void givenCapturingGroup_whenMatches_thenCorrect () {int maches = runTest ("(\ d \ d)", "12"); assertEquals (zápasy, 1); }

Číslo pripojené k vyššie uvedenej zhode je 1, pomocou spätnej referencie informujeme porovnávača, že chceme priradiť iný výskyt zhodenej časti textu. Týmto spôsobom namiesto:

@Test public void givenCapturingGroup_whenMatches_thenCorrect2 () {int zápasy = runTest ("(\ d \ d)", "1212"); assertEquals (zápasy, 2); }

Tam, kde pre vstup existujú dve samostatné zhody, môžeme mať jednu zhodu, ale šíriac tú istú zhodu regexu, aby sa rozpätie po celej dĺžke vstupu uskutočnilo pomocou spätných odkazov:

@Test public void givenCapturingGroup_whenMatchesWithBackReference_ thenCorrect () {int zápasy = runTest ("(\ d \ d) \ 1", "1212"); assertEquals (zápasy, 1); }

Kde by sme museli opakovať regulárny výraz bez spätných odkazov, aby sme dosiahli rovnaký výsledok:

@Test public void givenCapturingGroup_whenMatches_thenCorrect3 () {int zápasy = runTest ("(\ d \ d) (\ d \ d)", "1212"); assertEquals (zápasy, 1); }

Podobne pre akýkoľvek iný počet opakovaní môže spätná referencia spôsobiť, že porovnávač vidí vstup ako jednu zhodu:

@Test public void givenCapturingGroup_whenMatchesWithBackReference_ thenCorrect2 () {int zápasy = runTest ("(\ d \ d) \ 1 \ 1 \ 1", "12121212"); assertEquals (zápasy, 1); }

Ale ak zmeníte hoci len poslednú číslicu, zhoda nebude úspešná:

@Test public void givenCapturingGroupAndWrongInput_ whenMatchFailsWithBackReference_thenCorrect () {int zápasy = runTest ("(\ d \ d) \ 1", "1213"); assertFalse (zápasy> 0); }

Je dôležité nezabudnúť na únikové spätné lomky, čo je v syntaxi Java rozhodujúce.

10. Boundary Matchers

Rozhranie Java regex API tiež podporuje porovnávanie hraníc. Ak nám záleží na tom, kde presne vo vstupnom texte by sa mala zhoda vyskytnúť, potom to hľadáme. Pri predchádzajúcich príkladoch nám záležalo iba na tom, či sa zápas nájde alebo nie.

Aby sme sa zhodovali, iba ak je požadovaný regex pravdivý na začiatku textu, použijeme vsušku ^.

Tento test nebude úspešný, pretože text nebude k dispozícii pes nájdete na začiatku:

@Test public void givenText_whenMatchesAtBeginning_thenCorrect () {int zápasy = runTest ("^ pes", "psy sú priateľské"); assertTrue (zápasy> 0); }

Nasledujúci test zlyhá:

@Test public void givenTextAndWrongInput_whenMatchFailsAtBeginning_ thenCorrect () {int zápasy = runTest ("^ pes", "sú psy priateľské?"); assertFalse (zápasy> 0); }

Aby sme sa zhodovali, iba ak je požadovaný regex na konci textu pravdivý, použijeme znak dolára $. Zhoda sa nájde v nasledujúcom prípade:

@Test public void givenText_whenMatchesAtEnd_thenCorrect () {int zápasy = runTest ("pes $", "Najlepší priateľ človeka je pes"); assertTrue (zápasy> 0); }

A nenájde sa tu zhoda:

@Test public void givenTextAndWrongInput_whenMatchFailsAtEnd_thenCorrect () {int zápasy = runTest ("pes $", "je najlepší priateľ psa"?); assertFalse (zápasy> 0); }

Ak chceme zhodu, iba ak sa požadovaný text nachádza na hranici slova, použijeme \ b regex na začiatku a na konci regexu:

Medzera je hranicou slova:

@Test public void givenText_whenMatchesAtWordBoundary_thenCorrect () {int zápasy = runTest ("\ bdog \ b", "pes je priateľský"); assertTrue (zápasy> 0); }

Prázdny reťazec na začiatku riadku je tiež hranicou slova:

@Test public void givenText_whenMatchesAtWordBoundary_thenCorrect2 () {int zápasy = runTest ("\ bdog \ b", "pes je najlepším priateľom človeka"); assertTrue (zápasy> 0); }

Tieto testy vyhovujú, pretože začiatok a String, ako aj medzera medzi jedným textom a druhým, označuje hranicu slova, avšak nasledujúci test ukazuje pravý opak:

@Test public void givenWrongText_whenMatchFailsAtWordBoundary_thenCorrect () {int zápasy = runTest ("\ bdog \ b", "snoop dogg je rapper"); assertFalse (zápasy> 0); }

Dvojslovné znaky, ktoré sa objavujú v rade, neoznačujú hranicu slova, ale môžeme ju splniť tak, že zmeníme koniec regulárneho výrazu a vyhľadáme hranicu, ktorá nie je slovom:

@Test public void givenText_whenMatchesAtWordAndNonBoundary_thenCorrect () {int zápasy = runTest ("\ bdog \ B", "snoop dogg je rapper"); assertTrue (zápasy> 0); }

11. Metódy triedy vzoru

Predtým sme iba tvorili Vzor predmety základným spôsobom. Táto trieda má však iný variant zostaviť metóda, ktorá popri argumente regulárneho výrazu ovplyvňuje množinu príznakov ovplyvňujúcich spôsob zhody vzoru.

Tieto príznaky sú jednoducho abstraktné celočíselné hodnoty. Poďme preťažiť runTest metóda v testovacej triede, aby mohla brať príznak ako tretí argument:

public static int runTest (String regex, String text, int flags) {pattern = Pattern.compile (regex, flags); matcher = pattern.matcher (text); int zápasy = 0; while (matcher.find ()) {zodpovedá ++; } návratové zhody; }

V tejto časti sa pozrieme na rôzne podporované príznaky a na to, ako sa používajú.

Vzor.CANON_EQ

Tento príznak umožňuje kanonickú ekvivalenciu. Ak je zadané, budú sa považovať za zodpovedajúce dva znaky, len ak sa zhodujú ich úplné kanonické rozklady.

Zvážte znak Unicode s diakritikou é. Jeho zložený kódový bod je u00E9. Unicode má však pre svoje jednotlivé znaky aj samostatný kódový bod e, u0065 a akútny prízvuk, u0301. V tomto prípade zložený charakter u00E9 je nerozoznateľný od postupnosti dvoch znakov u0065 u0301.

Priraďovanie predvolene nezohľadňuje kanonickú ekvivalenciu:

@Test public void givenRegexWithoutCanonEq_whenMatchFailsOnEquivalentUnicode_thenCorrect () {int zápasy = runTest ("\ u00E9", "\ u0065 \ u0301"); assertFalse (zápasy> 0); }

Ale ak pridáme príznak, potom test prejde:

@Test public void givenRegexWithCanonEq_whenMatchesOnEquivalentUnicode_thenCorrect () {int zápasy = runTest ("\ u00E9", "\ u0065 \ u0301", Pattern.CANON_EQ); assertTrue (zápasy> 0); }

Vzor.CASE_INSENSITIVE

Tento príznak umožňuje zhodu bez ohľadu na veľkosť písmen. V predvolenom nastavení zhoda zohľadňuje prípady:

@Test public void givenRegexWithDefaultMatcher_whenMatchFailsOnDifferentCases_thenCorrect () {int zápasy = runTest ("pes", "Toto je pes"); assertFalse (zápasy> 0); }

Pomocou tohto príznaku teda môžeme zmeniť predvolené správanie:

@Test public void givenRegexWithCaseInsensitiveMatcher _whenMatchesOnDifferentCases_thenCorrect () {int zápasy = runTest ("pes", "Toto je pes", Pattern.CASE_INSENSITIVE); assertTrue (zápasy> 0); }

Na dosiahnutie rovnakého výsledku môžeme použiť aj ekvivalentný, vložený príznakový výraz:

@Test public void givenRegexWithEmbeddedCaseInsensitiveMatcher _whenMatchesOnDifferentCases_thenCorrect () {int zápasy = runTest ("(? I) pes", "Toto je pes"); assertTrue (zápasy> 0); }

Vzor. KOMENTÁRE

Java API umožňuje človeku zahrnúť komentáre pomocou # do regexu. To môže pomôcť pri dokumentovaní zložitých regulárnych výrazov, ktoré iný programátor nemusí okamžite poznať.

Príznak komentárov spôsobí, že matcher ignoruje všetky medzery alebo komentáre v regulárnom výraze a vezme do úvahy iba vzor. V predvolenom režime zhody by nasledujúci test zlyhal:

@Test public void givenRegexWithComments_whenMatchFailsWithoutFlag_thenCorrect () {int zápasy = runTest ("pes $ #check na konci textu" pes "," Toto je pes "); assertFalse (zápasy> 0); }

Je to tak preto, lebo hľadač vo vstupnom texte vyhľadá celý regulárny výraz vrátane medzier a znaku #. Ale keď použijeme príznak, bude ignorovať medzery navyše a každý text začínajúci sa # sa bude považovať za komentár, ktorý sa bude ignorovať pre každý riadok:

@Test public void givenRegexWithComments_whenMatchesWithFlag_thenCorrect () {int zápasy = runTest ("pes $ #check koniec textu", "Toto je pes", Pattern.COMMENTS); assertTrue (zápasy> 0); }

K tomu existuje aj alternatívny vložený výraz príznaku:

@Test public void givenRegexWithComments_whenMatchesWithEmbeddedFlag_thenCorrect () {int zápasy = runTest ("(? X) pes $ #check koniec textu", "Toto je pes"); assertTrue (zápasy> 0); }

Vzor. DOTALL

V predvolenom nastavení, keď používame bodku „.“ výraz v regulárnom výraze, zhodujeme sa s každým znakom na vstupe String až kým sa nestretneme s novým znakom riadku.

Pomocou tohto príznaku bude zápas obsahovať aj zakončenie riadku. Nasledujúcim príkladom to lepšie pochopíme. Tieto príklady budú trochu iné. Pretože máme záujem sa presadiť proti zhode String, použijeme dohadzovač‘S skupina metóda, ktorá vráti predchádzajúcu zhodu.

Najprv uvidíme predvolené správanie:

@Test public void givenRegexWithLineTerminator_whenMatchFails_thenCorrect () {Pattern pattern = Pattern.compile ("(. *)"); Matcher matcher = pattern.matcher ("toto je text" + System.getProperty ("line.separator") + "pokračovanie na inom riadku"); matcher.find (); assertEquals ("toto je text", matcher.group (1)); }

Ako vidíme, je zhodná iba prvá časť vstupu pred zakončovačom riadku.

Teraz v dotall režime sa zhoduje celý text vrátane zakončenia riadku:

@Test public void givenRegexWithLineTerminator_whenMatchesWithDotall_thenCorrect () {Pattern pattern = Pattern.compile ("(. *)", Pattern.DOTALL); Matcher matcher = pattern.matcher ("toto je text" + System.getProperty ("line.separator") + "pokračovanie na inom riadku"); matcher.find (); assertEquals ("toto je text" + System.getProperty ("line.separator") + "pokračovanie na inom riadku", matcher.group (1)); }

Na povolenie môžeme použiť aj vložený príznakový výraz dotall režim:

@Test public void givenRegexWithLineTerminator_whenMatchesWithEmbeddedDotall _thenCorrect () {Pattern pattern = Pattern.compile ("(? S) (. *)"); Matcher matcher = pattern.matcher ("toto je text" + System.getProperty ("line.separator") + "pokračovanie na inom riadku"); matcher.find (); assertEquals ("toto je text" + System.getProperty ("line.separator") + "pokračovanie na inom riadku", matcher.group (1)); }

Vzor. LITERÁLNY

V tomto režime nedáva Matcher žiadny špeciálny význam žiadnym metaznakom, únikovým znakom alebo syntaxi regulárneho výrazu. Bez tohto príznaku porovná matcher nasledujúci regulárny výraz s akýmkoľvek vstupom String:

@Test public void givenRegex_whenMatchesWithoutLiteralFlag_thenCorrect () {int zápasy = runTest ("(. *)", "Text"); assertTrue (zápasy> 0); }

Toto je predvolené správanie, ktoré sme videli vo všetkých príkladoch. S týmto príznakom sa však nenájde žiadna zhoda, pretože hľadač bude hľadať (.*) namiesto jeho tlmočenia:

@Test public void givenRegex_whenMatchFailsWithLiteralFlag_thenCorrect () {int zápasy = runTest ("(. *)", "Text", Pattern.LITERAL); assertFalse (zápasy> 0); }

Ak teraz pridáme požadovaný reťazec, test prejde:

@Test public void givenRegex_whenMatchesWithLiteralFlag_thenCorrect () {int zápasy = runTest ("(. *)", "Text (. *)", Pattern.LITERAL); assertTrue (zápasy> 0); }

Neexistuje žiadny vložený znak príznaku, ktorý by umožňoval literálnu analýzu.

Vzor. MULTILINE

Predvolene ^ a $ metaznaky sa zhodujú absolútne na začiatku a na konci celého vstupu String. Matcher ignoruje akékoľvek zakončovacie riadky:

@Test public void givenRegex_whenMatchFailsWithoutMultilineFlag_thenCorrect () {int zápasy = runTest ("pes $", "Toto je pes" + System.getProperty ("line.separator") + "toto je líška"); assertFalse (zápasy> 0); }

Zhoda zlyhá, pretože matcher hľadá pes na konci celej String ale pes sa nachádza na konci prvého riadku reťazca.

S príznakom však prejde rovnaká skúška, pretože hráč teraz berie do úvahy zakončovače riadkov. Takže String pes sa nachádza tesne pred ukončením riadku, z toho dôvodu úspech:

@Test public void givenRegex_whenMatchesWithMultilineFlag_thenCorrect () {int zápasy = runTest ("pes $", "Toto je pes" + System.getProperty ("line.separator") + "toto je líška", Pattern.MULTILINE); assertTrue (zápasy> 0); }

Tu je vložená verzia príznaku:

@Test public void givenRegex_whenMatchesWithEmbeddedMultilineFlag_ thenCorrect () {int zápasy = runTest ("(? M) pes $", "Toto je pes" + System.getProperty ("line.separator") + "toto je líška"); assertTrue (zápasy> 0); }

12. Metódy porovnávacích tried

V tejto časti sa pozrieme na niektoré užitočné metódy Matcher trieda. Z dôvodu prehľadnosti ich zoskupíme podľa funkčnosti.

12.1. Metódy indexovania

Metódy indexu poskytujú užitočné hodnoty indexu, ktoré presne ukazujú, kde sa vo vstupe našla zhoda String . V nasledujúcom teste potvrdíme počiatočné a koncové indexy zápasu pre pes vo vstupe String :

@Test public void givenMatch_whenGetsIndices_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = pattern.matcher ("Tento pes je môj"); matcher.find (); assertEquals (5, matcher.start ()); assertEquals (8, matcher.end ()); }

12.2. Metódy štúdia

Vstupom sú študijné metódy String a vráti booleovský znak označujúci, či sa vzor nájde alebo nie. Bežne sa používajú zápasy a pozerajúc sa na metódy.

The zápasy a pozerajúc sa na obe metódy sa pokúšajú spojiť vstupnú sekvenciu so vzorom. Rozdiel je v tom zápasy vyžaduje zhodu celej vstupnej sekvencie, zatiaľ čo pozerajúc sa na nie.

Obidva spôsoby začínajú na začiatku zadania String :

@Test public void whenStudyMethodsWork_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = pattern.matcher ("psy sú priateľské"); assertTrue (matcher.lookingAt ()); assertFalse (matcher.matches ()); }

Metóda zhody vráti hodnotu true v takom prípade:

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

12.3. Metódy výmeny

Metódy výmeny sú užitočné na nahradenie textu vo vstupnom reťazci. Bežné sú nahradiťPrvý a nahradiť všetko.

The nahradiťPrvý a nahradiť všetko metódy nahradzujú text, ktorý sa zhoduje s daným regulárnym výrazom. Ako naznačuje ich názov, nahradiťPrvý - nahrádza prvý výskyt a - nahradiť všetko nahradí všetky výskyty:

@Test public void whenReplaceFirstWorks_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = pattern.matcher ("psy sú domáce zvieratá, psy sú priateľské"); Reťazec newStr = matcher.replaceFirst ("mačka"); assertEquals („mačky sú domáce zvieratá, psy sú priateľské“, newStr); }

Nahradiť všetky výskyty:

@Test public void whenReplaceAllWorks_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = pattern.matcher ("psy sú domáce zvieratá, psy sú priateľské"); Reťazec newStr = matcher.replaceAll ("mačka"); assertEquals („mačky sú domáce zvieratá, mačky sú priateľské“, newStr); }

The nahradiť všetko metóda nám umožňuje nahradiť všetky zápasy rovnakou náhradou. Ak chceme nahradiť zápasy podľa jednotlivých prípadov, potrebovali by sme techniku ​​výmeny tokenov.

13. Záver

V tomto článku sme sa naučili, ako používať regulárne výrazy v Jave, a taktiež sme preskúmali najdôležitejšie vlastnosti java.util.regex balíček.

Celý zdrojový kód projektu vrátane všetkých tu použitých vzorových kódov nájdete v projekte GitHub.