Útmutató a Java rendszeres kifejezések API-jához

1. Áttekintés

Ebben a cikkben megvitatjuk a Java Regex API-t és azt, hogy a reguláris kifejezések miként használhatók a Java programozási nyelvben.

A reguláris kifejezések világában sokféle íz közül lehet választani, például grep, Perl, Python, PHP, awk és még sok más.

Ez azt jelenti, hogy az egyik programozási nyelven működő reguláris kifejezés nem biztos, hogy a másikban működik. A Java rendszeres kifejezés-szintaxisa leginkább hasonlít a Perl-hez.

2. Beállítás

A rendszeres kifejezések Java használatához nincs szükség külön beállításra. A JDK egy speciális csomagot tartalmaz java.util.regex teljesen a regex műveleteknek szentelt. Csak a kódunkba kell importálnunk.

Sőt, a java.lang.String osztály rendelkezik beépített regex támogatással is, amelyet gyakran használunk a kódunkban.

3. Java Regex csomag

A java.util.regex A csomag három osztályból áll: Minta, Matcher és PatternSyntaxException:

  • Minta object egy lefordított regex. A Minta osztály nem nyújt állami kivitelezőket. Egy minta létrehozásához először meg kell hívnunk annak egyik nyilvános statikáját összeállítani módszereket, amelyek ezután visszaadják a Minta tárgy. Ezek a módszerek első szabályként egy reguláris kifejezést fogadnak el.
  • Matcher objektum értelmezi a mintát, és egy bemenettel egyeztetési műveleteket hajt végre Húr. Ezenkívül nem határoz meg állami kivitelezőket. Megkapjuk a Matcher objektumot a páros módszer a Minta tárgy.
  • PatternSyntaxException Az objektum egy nem ellenőrzött kivétel, amely szintaktikai hibát jelez egy reguláris kifejezésmintában.

Részletesen megvizsgáljuk ezeket az osztályokat; azonban először meg kell értenünk, hogyan épül fel a regex a Java-ban.

Ha már ismeri a regexet egy másik környezetből, akkor bizonyos eltéréseket találhat, de ezek minimálisak.

4. Egyszerű példa

Kezdjük a regex legegyszerűbb felhasználási esetével. Amint azt korábban megjegyeztük, amikor egy regexet alkalmazunk egy karakterláncra, ez nulla vagy többször is megfelelhet.

A mintaillesztés legalapvetőbb formája, amelyet a java.util.regex Az API az meccs a Húr szó szerinti. Például, ha a reguláris kifejezés az foo és a bemenet Húr van foo, a meccs sikeres lesz, mert a Húrok azonosak:

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

Először létrehozunk egy Minta objektumot statikusnak nevezve összeállítani metódus és átadunk egy mintát, amelyet használni akarunk.

Ezután létrehozunk egy Matcher objektum hívja a Minta objektumé páros metódus és átadja azt a szöveget, amelyet ellenőrizni akarunk az egyezések szempontjából.

Ezt követően hívjuk meg a módszert megtalálja a Matcher objektumban.

A megtalálja A módszer folyamatosan halad előre a beviteli szövegen, és minden mérkőzésre igaz értéket ad, így felhasználhatjuk az egyezések számának megkeresésére is:

@Test public void givenText_whenSimpleRegexMatchesTwice_thenCorrect () {Pattern pattern = Pattern.compile ("foo"); Matcher matcher = minta.matcher ("foofoo"); int egyezik = 0; while (matcher.find ()) {egyezik ++; } assertEquals (mérkőzések, 2); }

Mivel több tesztet fogunk lefuttatni, elvonatkoztathatjuk a logikát az egyezések számának megtalálásához az úgynevezett módszerben runTest:

public static int runTest (String regex, String text) {Pattern pattern = Pattern.compile (regex); Matcher matcher = minta.matcher (szöveg); int egyezik = 0; while (matcher.find ()) {egyezik ++; } visszatérő mérkőzések; }

Ha 0 mérkőzést kapunk, akkor a tesztnek kudarcot kell vallania, különben át kell mennie.

5. Meta karakterek

A metakarakterek befolyásolják a minta illesztését, logikát adva a keresési mintához. A Java API több metakaraktert támogat, a legegyszerűbb a pont “.” bármely karakterhez illő:

@Test public void givenText_whenMatchesWithDotMetach_thenCorrect () {int mérkőzések = runTest (".", "Foo"); assertTrue (egyezik> 0); }

Figyelembe véve az előző példát, ahol a regex foo megfelelt a szövegnek foo továbbá foofoo kétszer. Ha a metex karaktert használnánk a regexben, akkor a második esetben nem kapnánk két mérkőzést:

@Test public void givenRepeatedText_whenMatchesOnceWithDotMetach_thenCorrect () {int mérkőzések = runTest ("foo.", "Foofoo"); assertEquals (mérkőzések, 1); }

Figyelje meg a pontot a foo a regexben. Az egyező minden szöveget egyezik, amelyet megelőz foo mivel az utolsó pont rész bármilyen karaktert jelent utána. Tehát miután megtalálta az elsőt foo, a többit bármilyen karakternek tekintik. Ezért van egyetlen meccs.

Az API számos más meta karaktert támogat amelyet ebben a cikkben tovább vizsgálunk.

6. Karakterosztályok

Böngészés a tisztviselőn Minta osztály specifikáció, a támogatott regex konstrukciók összefoglalóit fogjuk felfedezni. A karakterosztályok alatt körülbelül 6 konstrukciónk van.

6.1. VAGY Osztály

Úgy építették fel [ABC]. A készlet bármelyik eleme megegyezik:

@Test public void givenORSet_whenMatchesAny_thenCorrect () {int mérkőzések = runTest ("[abc]", "b"); assertEquals (mérkőzések, 1); }

Ha mindegyik megjelenik a szövegben, mindegyik külön-külön illeszkedik a sorrendre való tekintet nélkül:

@Test public void givenORSet_whenMatchesAnyAndAll_thenCorrect () {int mérkőzések = runTest ("[abc]", "cab"); assertEquals (mérkőzések, 3); }

Az a részeként váltakozhatnak is Húr. A következő példában, amikor különböző szavakat hozunk létre úgy, hogy az első betűt felváltjuk a halmaz minden elemével, akkor ezek mindegyike egyezik:

@Test public void givenORSet_whenMatchesAllCombinations_thenCorrect () {int mérkőzések = runTest ("[bcr] at", "denevér macska patkány"); assertEquals (mérkőzések, 3); }

6.2. SEM Osztály

A fenti készletet elutasítja, ha első elemként karátot ad hozzá:

@Test public void givenNORSet_whenMatchesNon_thenCorrect () {int mérkőzések = runTest ("[^ abc]", "g"); assertTrue (egyezik> 0); }

Egy másik eset:

@Test public void givenNORSet_whenMatchesAllExceptElements_thenCorrect () {int mérkőzések = runTest ("[^ bcr] at", "sat mat eat"); assertTrue (egyezik> 0); }

6.3. Tartományosztály

Definiálhatunk egy osztályt, amely kötőjel (-) segítségével meghatároz egy tartományt, amelyen belül az egyező szövegnek esnie kell, hasonlóképpen egy tartományt is tagadhatunk.

Nagybetűk egyezése:

@Test public void givenUpperCaseRange_whenMatchesUpperCase_ thenCorrect () {int mérkőzések = runTest ("[A-Z]", "Összesen két nagybetűs ábécé 34"); assertEquals (mérkőzések, 2); }

Kisbetűk egyezése:

@Test public void givenLowerCaseRange_whenMatchesLowerCase_ thenCorrect () {int mérkőzések = runTest ("[a-z]", "Összesen két nagybetűs ábécé 34"); assertEquals (mérkőzések, 26); }

Nagy- és kisbetűk egyeztetése:

@Test public void givenBothLowerAndUpperCaseRange_ whenMatchesAllLetters_thenCorrect () {int mérkőzések = runTest ("[a-zA-Z]", "Két nagybetűs ábécé 34 összesen"); assertEquals (mérkőzések, 28); }

Adott számtartomány illesztése:

@Test public void givenNumberRange_whenMatchesAccurately_ thenCorrect () {int mérkőzések = runTest ("[1-5]", "Két nagybetűs ábécé 34 összesen"); assertEquals (mérkőzések, 2); }

Egy másik számtartomány illesztése:

@Test public void givenNumberRange_whenMatchesAccurately_ thenCorrect2 () {int mérkőzések = runTest ("[30-35]", "Két nagybetűs ábécé 34 összesen"); assertEquals (mérkőzések, 1); }

6.4. Union osztály

Az unió karakterosztály két vagy több karakterosztály kombinálásának eredménye:

@Test public void givenTwoSets_whenMatchesUnion_thenCorrect () {int mérkőzések = runTest ("[1-3 [7-9]]", "123456789"); assertEquals (mérkőzések, 6); }

A fenti teszt csak a 9 egész számból 6-nak felel meg, mert az uniókészlet kihagyja a 4., 5. és 6. pontot.

6.5. Metszésosztály

Hasonlóan az unió osztályhoz, ez az osztály abból adódik, hogy két vagy több halmaz között közös elemeket választunk. A kereszteződés alkalmazásához a &&:

@Test public void givenTwoSets_whenMatchesIntersection_thenCorrect () {int mérkőzések = runTest ("[1-6 && [3-9]]", "123456789"); assertEquals (mérkőzések, 4); }

4 mérkőzést kapunk, mert a két halmaz metszéspontjában csak 4 elem van.

6.6. Kivonási osztály

Kivonással feloldhatunk egy vagy több karakterosztályt, például illeszkedhetünk páratlan tizedesszámok halmazához:

@Test public void givenSetWithSubtraction_whenMatchesAccurately_thenCorrect () {int mérkőzések = runTest ("[0-9 && [^ 2468]]", "123456789"); assertEquals (mérkőzések, 5); }

Csak 1,3,5,7,9 párosul.

7. Előre definiált karakterosztályok

A Java regex API előre definiált karakterosztályokat is elfogad. A fenti karakterosztályok egy része rövidebb formában is kifejezhető, bár a kód kevésbé intuitív. A regex Java változatának egyik speciális aspektusa a escape karakter.

Amint látni fogjuk, a legtöbb karakter egy visszavágással kezdődik, amelynek különleges jelentése van a Java-ban. Ahhoz, hogy ezeket összeállítsa a Minta osztály - el kell kerülni a vezető visszavonást, azaz \ d válik \ d.

Megegyező számjegyek, egyenértékűek [0-9]:

@Test public void givenDigits_whenMatches_thenCorrect () {int mérkőzések = runTest ("\ d", "123"); assertEquals (mérkőzések, 3); }

Nem számjegyek egyezése, egyenértékű [^0-9]:

@Test public void givenNonDigits_whenMatches_thenCorrect () {int mathces = runTest ("\ D", "a6c"); assertEquals (mérkőzések, 2); }

Összeférő szóköz:

@Test public void givenWhiteSpace_whenMatches_thenCorrect () {int mérkőzések = runTest ("\ s", "a c"); assertEquals (mérkőzések, 1); }

A nem fehér tér megfeleltetése:

@Test public void givenNonWhiteSpace_whenMatches_thenCorrect () {int mérkőzések = runTest ("\ S", "a c"); assertEquals (mérkőzések, 2); }

A szó karakterének megfelelő, egyenértékű a következővel: [a-zA-Z_0-9]:

@Test public void givenWordCharacter_whenMatches_thenCorrect () {int mérkőzések = runTest ("\ w", "szia!"); assertEquals (mérkőzések, 2); }

Nem szó szerinti karakter illesztése:

@Test public void givenNonWordCharacter_whenMatches_thenCorrect () {int mérkőzések = runTest ("\ W", "szia!"); assertEquals (mérkőzések, 1); }

8. Kvantorok

A Java regex API lehetővé teszi számszerűsítők használatát is. Ezek lehetővé teszik számunkra, hogy tovább módosítsuk a meccs viselkedését azáltal, hogy meghatározzuk a megfelelő események számát.

Nulla vagy egyszeri szöveg egyezéséhez a ? kvantor:

@Test public void givenZeroOrOneQuantifier_whenMatches_thenCorrect () {int mérkőzések = runTest ("\ a?", "Szia"); assertEquals (mérkőzések, 3); }

Alternatív megoldásként használhatjuk a merevítő szintaxist, amelyet a Java regex API is támogat:

@Test public void givenZeroOrOneQuantifier_whenMatches_thenCorrect2 () {int mérkőzések = runTest ("\ a {0,1}", "szia"); assertEquals (mérkőzések, 3); }

Ez a példa bevezeti a nulla hosszúságú mérkőzések fogalmát. Így fordulhat elő, hogy ha egy kvantor egyezési küszöbértéke nulla, akkor mindig minden egyezik a szövegben, beleértve az üreset is Húr minden bemenet végén. Ez azt jelenti, hogy még akkor is, ha a bemenet üres, egy nulla hosszúságú egyezést ad vissza.

Ez megmagyarázza, hogy miért kapunk 3 találatot a fenti példában annak ellenére, hogy van String kettő hosszúságú. A harmadik mérkőzés nulla hosszú üres Húr.

Ahhoz, hogy a szöveg nulla vagy korlátlan idők legyenek, mi * kvantor, csak hasonlít a?:

@Test public void givenZeroOrManyQuantifier_whenMatches_thenCorrect () {int mérkőzések = runTest ("\ a *", "szia"); assertEquals (mérkőzések, 3); }

Támogatott alternatíva:

@Test public void givenZeroOrManyQuantifier_whenMatches_thenCorrect2 () {int mérkőzések = runTest ("\ a {0,}", "szia"); assertEquals (mérkőzések, 3); }

A különbséggel rendelkező kvantor +, megfelelő küszöbértéke 1. Ha szükséges Húr egyáltalán nem fordul elő, nem lesz meccs, még nulla hosszúságú sem Húr:

@Test public void givenOneOrManyQuantifier_whenMatches_thenCorrect () {int mérkőzések = runTest ("\ a +", "szia"); assertFalse (egyezik); }

Támogatott alternatíva:

@Test public void givenOneOrManyQuantifier_whenMatches_thenCorrect2 () {int mérkőzések = runTest ("\ a {1,}", "szia"); assertFalse (egyezik); }

Mint a Perl-ben és más nyelveken, a zárójel-szintaxis is használható egy adott szöveg többszörös megfeleltetésére:

@Test public void givenBraceQuantifier_whenMatches_thenCorrect () {int mérkőzések = runTest ("a {3}", "aaaaaa"); assertEquals (mérkőzések, 2); }

A fenti példában két mérkőzést kapunk, mivel egyezés csak akkor következik be, ha a háromszor jelenik meg egymás után. A következő tesztben azonban nem kapunk egyezést, mivel a szöveg csak kétszer jelenik meg egymás után:

@Test public void givenBraceQuantifier_whenFailsToMatch_thenCorrect () {int mérkőzések = runTest ("a {3}", "aa"); assertFalse (egyezik> 0); }

Ha a zárójelben egy tartományt használunk, akkor a mérkőzés kapzsi lesz, a tartomány felső végéhez illeszkedve:

@Test public void givenBraceQuantifierWithRange_whenMatches_thenCorrect () {int mérkőzések = runTest ("a {2,3}", "aaaa"); assertEquals (mérkőzések, 1); }

Legalább két előfordulást megadtunk, de nem haladta meg a hármat, így egyetlen mérkőzést kapunk, ahelyett, hogy a mérkőzõ egyet látna aaa és a magányos, amihez nem lehet párosítani.

Az API azonban lehetővé teszi számunkra, hogy meghatározzon egy lusta vagy vonakodó megközelítést, hogy az illesztő a tartomány alsó végéből indulhasson, amely esetben két előfordulást illeszthet aa és aa:

@Test public void givenBraceQuantifierWithRange_whenMatchesLazily_thenCorrect () {int mérkőzések = runTest ("a {2,3}?", "Aaaa"); assertEquals (mérkőzések, 2); }

9. Csoportok elfogása

Az API is lehetővé teszi számunkra több karaktert egyetlen egységként kezel a csoportok rögzítésével.

Számokat csatol a rögzítő csoportokhoz, és lehetővé teszi a visszautalást ezen számok használatával.

Ebben a részben néhány példát fogunk látni a csoportok rögzítésének használatáról a Java regex API-ban.

Használjunk olyan rögzítési csoportot, amely csak akkor egyezik, ha egy beviteli szöveg két számjegyet tartalmaz egymás mellett:

@Test public void givenCapturingGroup_whenMatches_thenCorrect () {int maches = runTest ("(\ d \ d)", "12"); assertEquals (mérkőzések, 1); }

A fenti találathoz csatolt szám: 1, egy hátsó hivatkozás segítségével mondja el az egyezőnek, hogy egyeztetni akarjuk a szöveg egyeztetett részének egy másik előfordulását. Így, ahelyett, hogy:

@Test public void givenCapturingGroup_whenMatches_thenCorrect2 () {int mérkőzések = runTest ("(\ d \ d)", "1212"); assertEquals (mérkőzések, 2); }

Ahol két külön egyezés van a bemenethez, akkor lehet egy egyezésünk, de ugyanazt a regex egyezést terjesztjük, hogy a visszahivatkozás segítségével a bemenet teljes hosszát lefedje:

@Test public void givenCapturingGroup_whenMatchesWithBackReference_ thenCorrect () {int mérkőzések = runTest ("(\ d \ d) \ 1", "1212"); assertEquals (mérkőzések, 1); }

Ahol ugyanazon eredmény elérése érdekében ismételnünk kellene a regexet vissza hivatkozás nélkül:

@Test public void givenCapturingGroup_whenMatches_thenCorrect3 () {int mérkőzések = runTest ("(\ d \ d) (\ d \ d)", "1212"); assertEquals (mérkőzések, 1); }

Hasonlóképpen, bármely más ismétlés esetén a visszahivatkozás arra késztetheti a mérkőzést, hogy a bemenet egyetlen egyezésként legyen látható:

@Test public void givenCapturingGroup_whenMatchesWithBackReference_ thenCorrect2 () {int mérkőzések = runTest ("(\ d \ d) \ 1 \ 1 \ 1", "12121212"); assertEquals (mérkőzések, 1); }

De ha még az utolsó számjegyet is megváltoztatja, a mérkőzés sikertelen lesz:

@Test public void givenCapturingGroupAndWrongInput_ whenMatchFailsWithBackReference_thenCorrect () {int mérkőzések = runTest ("(\ d \ d) \ 1", "1213"); assertFalse (egyezik> 0); }

Fontos, hogy ne feledkezzünk meg a menekülési visszavágásokról, ez döntő fontosságú a Java szintaxisában.

10. Határmérkőzések

A Java regex API támogatja a határok egyeztetését is. Ha érdekel, hogy a beviteli szövegben pontosan hol kell bekövetkeznie az egyezésnek, akkor ezt keressük. Az előző példákkal csak azzal törődtünk, hogy találnak-e meccset vagy sem.

Csak akkor egyezünk, ha a szükséges regex a szöveg elején igaz, akkor a caret-et használjuk ^.

Ez a teszt a szöveg óta sikertelen lesz kutya elején található:

@Test public void givenText_whenMatchesAtBeginning_thenCorrect () {int mérkőzések = runTest ("^ kutya", "a kutyák barátságosak"); assertTrue (egyezik> 0); }

A következő teszt sikertelen lesz:

@Test public void givenTextAndWrongInput_whenMatchFailsAtBeginning_ thenCorrect () {int mérkőzések = runTest ("^ kutya", "a kutyák barátságosak?"); assertFalse (egyezik> 0); }

Csak akkor egyezünk, ha a szükséges regex a szöveg végén igaz, a dollár karaktert használjuk $. Egyezés a következő esetben található:

@Test public void givenText_whenMatchesAtEnd_thenCorrect () {int mérkőzések = runTest ("kutya $", "Az ember legjobb barátja kutya"); assertTrue (egyezik> 0); }

És itt nem található egyezés:

@Test public void givenTextAndWrongInput_whenMatchFailsAtEnd_thenCorrect () {int mérkőzések = runTest ("kutya $", "egy kutyaember legjobb barátja?"); assertFalse (egyezik> 0); }

Ha csak akkor akarunk egyezést, ha a szükséges szöveg megtalálható egy szóhatáron, akkor azt használjuk \ b regex a regex elején és végén:

A szó egy szóhatár:

@Test public void givenText_whenMatchesAtWordBoundary_thenCorrect () {int mérkőzések = runTest ("\ bdog \ b", "a kutya barátságos"); assertTrue (egyezik> 0); }

A sor elején lévő üres karakterlánc szintén szóhatár:

@Test public void givenText_whenMatchesAtWordBoundary_thenCorrect2 () {int mérkőzések = runTest ("\ bdog \ b", "kutya az ember legjobb barátja"); assertTrue (egyezik> 0); }

Ezek a tesztek sikeresek, mert az a kezdete Húr, valamint az egyik szöveg és a másik közötti szóköz jelöli a szóhatárokat, azonban a következő teszt az ellenkezőjét mutatja:

@Test public void givenWrongText_whenMatchFailsAtWordBoundary_thenCorrect () {int mérkőzések = runTest ("\ bdog \ b", "snoop dogg egy rapper"); assertFalse (egyezik> 0); }

A sorban megjelenő kétszavas karakterek nem jelölik meg a szóhatárokat, de átengedhetjük őket azzal, hogy megváltoztatjuk a regex végét úgy, hogy nem szóhatárokat keressünk:

@Test public void givenText_whenMatchesAtWordAndNonBoundary_thenCorrect () {int mérkőzések = runTest ("\ bdog \ B", "snoop dogg egy rapper"); assertTrue (egyezik> 0); }

11. Mintaosztály módszerek

Korábban csak hoztunk létre Minta tárgyak alapvető módon. Ennek az osztálynak azonban van egy másik változata is összeállítani metódus, amely a jelek halmazát fogadja el a regex argumentum mellett, amely befolyásolja a minta illesztését.

Ezek a jelölők egyszerűen absztrakt egész számok. Túlterheljük a runTest módszer a teszt osztályban, hogy harmadik argumentumként jelzőt vehessen fel:

public static int runTest (String regex, String text, int flags) {minta = Pattern.compile (regex, zászlók); egyező = minta.matcher (szöveg); int egyezik = 0; while (matcher.find ()) {egyezik ++; } visszatérő mérkőzések; }

Ebben a részben megvizsgáljuk a különböző támogatott jelzőket és azok használatát.

Minta.CANON_EQ

Ez a zászló lehetővé teszi a kanonikus egyenértékűséget. Ha meg van határozva, akkor két karakter akkor tekinthető egyezőnek, ha csak teljes kanonikus bontásuk megegyezik.

Vegye figyelembe az ékezetes Unicode karaktert é. Összetett kódpontja az u00E9. Az Unicode-nak azonban van egy külön kódpontja is az összetevő karakterei számára e, u0065 és az éles akcentus, u0301. Ebben az esetben összetett karakter u00E9 nem különböztethető meg a két karaktersortól u0065 u0301.

Alapértelmezés szerint az egyezés nem veszi figyelembe a kanonikus egyenértékűséget:

@Test public void givenRegexWithoutCanonEq_whenMatchFailsOnEquivalentUnicode_thenCorrect () {int mérkőzések = runTest ("\ u00E9", "\ u0065 \ u0301"); assertFalse (egyezik> 0); }

De ha hozzáadjuk a zászlót, akkor a teszt sikeres lesz:

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

Minta.CASE_INSENSITIVE

Ez a zászló lehetővé teszi az egyezést az esetektől függetlenül. Alapértelmezés szerint az egyezés figyelembe veszi az eseteket:

@Test public void givenRegexWithDefaultMatcher_whenMatchFailsOnDifferCases_thenCorrect () {int mérkőzések = runTest ("kutya", "Ez egy kutya"); assertFalse (egyezik> 0); }

Tehát ezzel a zászlóval megváltoztathatjuk az alapértelmezett viselkedést:

@Test public void givenRegexWithCaseInsensitiveMatcher _whenMatchesOnDifferentCases_thenCorrect () {int mérkőzések = runTest ("kutya", "Ez egy kutya", Minta.CASE_INSENSITIVE); assertTrue (egyezik> 0); }

Használhatjuk az egyenértékű, beágyazott jelzőkifejezést is ugyanazon eredmény eléréséhez:

@Test public void givenRegexWithEmbeddedCaseInsensitiveMatcher _whenMatchesOnDifferCases_thenCorrect () {int mérkőzések = runTest ("(? I) kutya", "Ez egy kutya"); assertTrue (egyezik> 0); }

Minta.MEGJEGYZÉSEK

A Java API lehetővé teszi a # használatával a regexbe történő megjegyzések felvételét. Ez segíthet a komplex regex dokumentálásában, amely nem feltétlenül egyértelmű egy másik programozó számára.

A Megjegyzések jelző arra készteti a mérkőzést, hogy figyelmen kívül hagyja a szóközöket vagy a regexben szereplő megjegyzéseket, és csak a mintát veszi figyelembe. Az alapértelmezett illesztési módban a következő teszt sikertelen lesz:

@Test public void givenRegexWithComments_whenMatchFailsWithoutFlag_thenCorrect () {int mérkőzések = runTest ("dog $ #check for word dog a szöveg végén", "Ez egy kutya"); assertFalse (egyezik> 0); }

Ennek oka, hogy az illesztő megkeresi a teljes regexet a beviteli szövegben, beleértve a szóközöket és a # karaktert is. De amikor a zászlót használjuk, az figyelmen kívül hagyja az extra szóközöket, és a # -el kezdődő minden szöveget minden sor figyelmen kívül hagyandó megjegyzésének tekintjük:

@Test public void givenRegexWithComments_whenMatchesWithFlag_thenCorrect () {int mérkőzések = runTest ("dog $ #check text end", "This is a dog", Pattern.COMMENTS); assertTrue (egyezik> 0); }

Van egy alternatív beágyazott jelző kifejezés is ehhez:

@Test public void givenRegexWithComments_whenMatchesWithEmbeddedFlag_thenCorrect () {int mérkőzések = runTest ("(? X) kutya $ #check a szöveg vége", "Ez egy kutya"); assertTrue (egyezik> 0); }

Minta.DOTALL

Alapértelmezés szerint, amikor a „.” Pontot használjuk kifejezés a regexben, a bemenet minden karakterét egyeztetjük Húr amíg új vonalkarakterrel nem találkozunk.

Ezt a zászlót használva a meccs tartalmazni fogja a vonalzárót is. Jobban megértjük a következő példákkal. Ezek a példák kicsit mások lesznek. Mivel érdekeltek vagyunk a mérkőzések elleni érvényesítésben Húr, használni fogjuk páros’S csoport metódus, amely az előző mérkőzést adja vissza.

Először látni fogjuk az alapértelmezett viselkedést:

@Test public void givenRegexWithLineTerminator_whenMatchFails_thenCorrect () {Pattern pattern = Pattern.compile ("(. *)"); Matcher matcher = pattern.matcher ("ez egy szöveg" + System.getProperty ("line.separator") + "egy másik soron folytatódik"); matcher.find (); assertEquals ("ez egy szöveg", matcher.group (1)); }

Mint láthatjuk, csak a bemenet első része illeszkedik a vonalvégző előtt.

Most dotall módban a teljes szöveg, a sorvégződést is beleértve:

@Test public void givenRegexWithLineTerminator_whenMatchesWithDotall_thenCorrect () {Pattern pattern = Pattern.compile ("(. *)", Pattern.DOTALL); Matcher matcher = pattern.matcher ("ez egy szöveg" + System.getProperty ("line.separator") + "egy másik soron folytatódik"); matcher.find (); assertEquals ("ez egy szöveg" + System.getProperty ("line.separator") + "egy másik soron folytatódik", matcher.group (1)); }

Az engedélyezéshez beágyazott jelzőkifejezést is használhatunk dotall mód:

@Test public void givenRegexWithLineTerminator_whenMatchesWithEmbeddedDotall _thenCorrect () {Pattern pattern = Pattern.compile ("(? S) (. *)"); Matcher matcher = pattern.matcher ("ez egy szöveg" + System.getProperty ("line.separator") + "egy másik soron folytatódik"); matcher.find (); assertEquals ("ez egy szöveg" + System.getProperty ("line.separator") + "egy másik soron folytatódik", matcher.group (1)); }

Minta.LITERAL

Ebben a módban a matcher nem ad különösebb jelentést metakaraktereknek, menekülési karaktereknek vagy regex szintaxisnak. E zászló nélkül a mérkőzõ a következõ regexet illeszti bármilyen bemenethez Húr:

@Test public void givenRegex_whenMatchesWithoutLiteralFlag_thenCorrect () {int mérkőzések = runTest ("(. *)", "Text"); assertTrue (egyezik> 0); }

Ez az alapértelmezett viselkedés, amelyet minden példában láttunk. Ezzel a zászlóval azonban nem fog találni mérkőzést, mivel a páros keresni fog (.*) értelmezés helyett:

@Test public void givenRegex_whenMatchFailsWithLiteralFlag_thenCorrect () {int mérkőzések = runTest ("(. *)", "Text", Pattern.LITERAL); assertFalse (egyezik> 0); }

Ha hozzáadjuk a szükséges karakterláncot, a teszt sikeres lesz:

@Test public void givenRegex_whenMatchesWithLiteralFlag_thenCorrect () {int mérkőzések = runTest ("(. *)", "Text (. *)", Minta.LITERAL); assertTrue (egyezik> 0); }

Nincs beágyazott jelző karakter a szó szerinti elemzés engedélyezéséhez.

Minta.MULTILINE

Alapértelmezés szerint ^ és $ a metakarakterek abszolút egyeznek a teljes bemenet elején, illetve végén Húr. A meccselő figyelmen kívül hagyja a vonalzárókat:

@Test public void givenRegex_whenMatchFailsWithoutMultilineFlag_thenCorrect () {int mérkőzések = runTest ("dog $", "Ez egy kutya" + System.getProperty ("line.separator") + "ez egy róka"); assertFalse (egyezik> 0); }

A mérkőzés kudarcot vall, mert a páros keres kutya az egész végén Húr de a kutya a karakterlánc első sorának végén van.

A zászlóval azonban ugyanaz a teszt sikeres lesz, mivel a meccselő most figyelembe veszi a vonalzárókat. Tehát a húr kutya közvetlenül a vonal befejezése előtt található, így a siker:

@Test public void givenRegex_whenMatchesWithMultilineFlag_thenCorrect () {int mérkőzések = runTest ("dog $", "Ez egy kutya" + System.getProperty ("line.separator") + "ez egy róka", Pattern.MULTILINE); assertTrue (egyezik> 0); }

Itt van a beágyazott zászló verzió:

@Test public void givenRegex_whenMatchesWithEmbeddedMultilineFlag_ thenCorrect () {int mérkőzések = runTest ("(? M) dog $", "Ez egy kutya" + System.getProperty ("line.separator") + "ez egy róka"); assertTrue (egyezik> 0); }

12. Matcher osztály módszerek

Ebben a részben megvizsgáljuk a Matcher osztály. Az egyértelműség érdekében a funkcionalitás szerint csoportosítjuk őket.

12.1. Index módszerek

Az indexes módszerek hasznos indexértékeket nyújtanak, amelyek pontosan megmutatják, hogy a bemenetben hol található az egyezés Húr . A következő tesztben megerősítjük a mérkőzés kezdő és befejező indexét kutya a bemenetben Húr :

@Test public void givenMatch_whenGetsIndices_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = minta.matcher ("Ez a kutya az enyém"); matcher.find (); assertEquals (5, matcher.start ()); assertEquals (8, matcher.end ()); }

12.2. Tanulmányi módszerek

A tanulmányi módszerek átmennek a bemeneten Húr és adjon vissza logikai értéket, jelezve, hogy a minta megtalálható-e. Általánosan használtak mérkőzések és ránéz mód.

A mérkőzések és ránéz A metódusok mindkét esetben megkísérlik egy bemeneti szekvenciát egy mintához igazítani. A különbség az mérkőzések megköveteli a teljes bemeneti szekvencia egyeztetését, míg ránéz nem.

Mindkét módszer a bemenet elején kezdődik Húr :

@Test public void whenStudyMethodsWork_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = minta.matcher ("a kutyák barátságosak"); assertTrue (matcher.lookingAt ()); assertFalse (matcher.matches ()); }

A mérkőzések metódusa igaz lesz ilyen esetben:

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

12.3. Cseremódszerek

A helyettesítési módszerek hasznosak a beviteli karakterlánc szövegének helyettesítésére. A közösek azok firstFirst és csereAll.

A firstFirst és csereAll metódusok helyettesítik az adott reguláris kifejezésnek megfelelő szöveget. Ahogy a nevük is jelzi, firstFirst helyettesíti az első előfordulást, és csereAll minden előfordulást felvált:

@Test public void whenReplaceFirstWorks_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = minta.matcher ("a kutyák háziállatok, a kutyák barátságosak"); String newStr = matcher.replaceFirst ("macska"); assertEquals ("a macskák háziállatok, a kutyák barátságosak", newStr); }

Cserélje le az összes előfordulást:

@Test public void whenReplaceAllWorks_thenCorrect () {Pattern pattern = Pattern.compile ("dog"); Matcher matcher = minta.matcher ("a kutyák háziállatok, a kutyák barátságosak"); String newStr = matcher.replaceAll ("macska"); assertEquals ("a macskák háziállatok, a macskák barátságosak", newStr); }

A csereAll módszer lehetővé teszi számunkra, hogy az összes mérkőzést ugyanazzal a helyettesítéssel helyettesítsük. Ha eseti alapon szeretnénk lecserélni a meccseket, szükségünk lenne egy tokencserére.

13. Következtetés

Ebben a cikkben megtanultuk a reguláris kifejezések használatát a Java-ban, és feltártuk a java.util.regex csomag.

A projekt teljes forráskódja az összes itt használt kódmintával együtt megtalálható a GitHub projektben.