Hogyan használjuk a reguláris kifejezéseket a tokenek cseréjére a Java karakterláncokban

1. Áttekintés

Amikor meg kell találnunk vagy cserélnünk kell egy értéket egy sztringben a Java-ban, akkor általában reguláris kifejezéseket használunk. Ezek lehetővé teszik számunkra, hogy megállapítsuk, egy vagy több karakterlánc megfelel-e egy mintának. Mi talán könnyen alkalmazza ugyanazt a helyettesítést egy karakterlánc több tokenjére a csereAll módszer mindkettőben Matcher és Húr.

Ebben az oktatóanyagban azt vizsgáljuk meg, hogyan lehet más helyettesítést alkalmazni az egyes karakterláncokban található tokenek számára. Ez megkönnyíti számunkra a felhasználási esetek kielégítését, például bizonyos karakterek elhagyása vagy helyőrző értékek cseréje.

Megvizsgálunk néhány trükköt a reguláris kifejezéseink hangolásához a tokenek helyes azonosításához.

2. A mérkőzések egyéni feldolgozása

Mielőtt felépíthetnénk a token-token csere algoritmust, meg kell értenünk a Java API-t a reguláris kifejezések köré. Oldjunk meg egy trükkös egyeztetési problémát elfogó és nem elfogó csoportok segítségével.

2.1. Cím Eset Példa

Képzeljük el, hogy egy algoritmust szeretnénk felépíteni az összes címszó feldolgozására egy karakterláncban. Ezek a szavak egy nagybetűvel kezdődnek, majd vagy csak kisbetűvel végződnek, vagy folytatódnak.

Bevitelünk a következő lehet:

"Először 3 nagybetűs szó, majd 10 TLA, megtaláltam"

A címszó meghatározásából ez tartalmazza az egyezéseket:

  • Első
  • Főváros
  • Szavak
  • én
  • Megtalált

És ennek a mintának a felismerésére rendszeres kifejezés lenne:

"(? <= ^ | [^ A-Za-z]) ([A-Z] [a-z] *) (? = [^ A-Za-z] | $)"

Ennek megértéséhez bontsuk szét az alkatrészekre. Középen kezdjük:

[A-Z]

egyetlen nagybetűt ismer fel.

Megengedjük az egy karakterből álló szavakat vagy szavakat, amelyeket kisbetű követ, így:

[a-z] *

nulla vagy több kisbetűt ismer fel.

Bizonyos esetekben a fenti két karakterosztály elegendő a tokenek felismeréséhez. Sajnos példaszövegünkben van egy szó, amely több nagybetűvel kezdődik. Ebből kifolyólag, ki kell fejeznünk, hogy az általunk talált egyetlen nagybetűnek kell elsőként megjelennie a nem betűk után.

Hasonlóképpen, mivel megengedünk egyetlen nagybetűs szót, ki kell fejeznünk, hogy az általunk talált egyetlen nagybetű nem lehet az első a nagybetűs szavak közül.

A kifejezés [^ A-Za-z] jelentése „nincs betű”. Ezek egyikét a kifejezés elejére tettük egy nem elfogó csoportba:

(? <= ^ | [^ A-Za-z])

A nem elfogó csoport kezdve (?<=, a hátranézés annak biztosítása érdekében, hogy a mérkőzés a megfelelő határon jelenjen meg. A végén lévő megfelelője ugyanazt a munkát végzi az utána következő karakterekkel.

Ha azonban a szavak a karakterlánc legelejét vagy végét érintik, akkor ezt figyelembe kell vennünk, itt adtuk hozzá a ^ | az első csoporthoz, hogy ez a "karakterlánc vagy bármilyen nem betűs karakter kezdetét" jelentse, és az utolsó nem befogó csoport végére felvettük a $ -t, hogy a karakterlánc vége határ .

A nem elfogó csoportokban talált karakterek nem jelennek meg a mérkőzésen amikor használjuk megtalálja.

Meg kell jegyeznünk, hogy még egy ilyen egyszerű használati esetnek is sok éles esete lehet, tehát fontos, hogy teszteljük a reguláris kifejezéseket. Ehhez írhatunk egységteszteket, használhatjuk az IDE beépített eszközeit, vagy használhatunk olyan online eszközt, mint a Regexr.

2.2. Példánk tesztelése

Példaszövegünkkel állandónak nevezzük EXAMPLE_INPUT és rendszeres kifejezésünk a Minta hívott TITLE_CASE_PATTERN, használjuk megtalálja a Matcher osztály, hogy az összes meccsünket egyetlen teszten kivonja:

Matcher matcher = TITLE_CASE_PATTERN.matcher (EXAMPLE_INPUT); Lista egyezés = new ArrayList (); while (matcher.find ()) {match.add (matcher.group (1)); } assertThat (egyezik) .conconExactly ("Első", "Nagybetű", "Szavak", "I", "Talált");

Itt használjuk a páros funkció be van kapcsolva Minta előállítani a Matcher. Akkor használjuk a megtalálja metódus egy ciklusban, amíg abbahagyja a visszatérést igaz hogy az összes mérkőzést iterálják.

Minden alkalommal megtalálja visszatér igaz, a Matcher Az objektum állapota az aktuális egyezést képviseli. Az egész meccset megvizsgálhatjuk csoport (0)vagy megvizsgálja az egyes befogó csoportokat 1-es indexükkel. Ebben az esetben van egy rögzítő csoport a kívánt darab körül, ezért használjuk csoport (1) hogy felvegyük a mérkőzést a listánkra.

2.3. Ellenőrzés Matcher kicsit több

Eddig sikerült megtalálni a feldolgozni kívánt szavakat.

Ha azonban ezek a szavak mindegyike egy jelző, amelyet le akartunk cserélni, akkor a kapott karakterlánc felépítéséhez több információra van szükségünk a mérkőzésről. Vizsgáljuk meg a Matcher ez segíthet nekünk:

while (matcher.find ()) {System.out.println ("Match:" + matcher.group (0)); System.out.println ("Start:" + matcher.start ()); System.out.println ("Vége:" + matcher.end ()); }

Ez a kód megmutatja, hogy hol vannak az egyes mérkőzések. Ez megmutatja nekünk a csoport (0) mérkőzés, ami mindent elfog:

Meccs: Első kezdet: 0 Vége: 5 Meccs: Fővárosi kezdet: 8 Vége: 15 Mérkőzés: Szavak kezdete: 16 Vége: 21 Mérkőzés: I Kezdés: 37 Vége: 38 ... tovább

Itt láthatjuk, hogy minden mérkőzés csak azokat a szavakat tartalmazza, amelyekre számítunk. A Rajt tulajdonság a mérkőzés nulla alapú indexét mutatja a húron belül. A vége a karakter indexét mutatja közvetlenül utána. Ez azt jelenti, hogy használhatnánk szubsztring (kezdet, vég kezdet) hogy minden gyufát kivonjon az eredeti karakterláncból. Lényegében így működik a csoport módszer ezt teszi helyettünk.

Most, hogy használhatjuk megtalálja a mérkőzések iterációjához dolgozzuk fel a jeleinket.

3. A gyufák egyesével történő cseréje

Folytassuk a példánkat azzal, hogy algoritmusunkkal kicseréljük az eredeti karakterlánc minden címszavát kisbetűvel. Ez azt jelenti, hogy a tesztláncunkat a következőkké alakítjuk:

"először 3 nagybetűs szó, aztán 10 TLA, találtam"

A Minta és Matcher osztály ezt nem teheti meg helyettünk, ezért össze kell állítanunk egy algoritmust.

3.1. A csere algoritmus

Itt van az algoritmus álkódja:

  • Kezdje egy üres kimeneti karakterlánccal
  • Minden mérkőzésre:
    • Adja hozzá a kimenethez bármit, ami a meccs előtt és minden korábbi meccs után következett
    • Feldolgozza ezt a mérkőzést, és adja hozzá a kimenethez
    • Addig folytassa, amíg az összes mérkőzés feldolgozásra nem kerül
    • Adjon hozzá bármit, ami az utolsó meccs után maradt

Meg kell jegyeznünk, hogy ennek az algoritmusnak a célja az keresse meg az összes nem egyeztetett területet, és adja hozzá a kimenethez, valamint hozzáadja a feldolgozott egyezéseket.

3.2. A Token Replacer Java-ban

Minden szót kisbetűvé szeretnénk alakítani, így egyszerű konverziós módszert írhatunk:

privát statikus karakterlánc-konvertálás (String token) {return token.toLowerCase (); }

Most megírhatjuk az algoritmust a mérkőzések iterációjára. Ez használhatja a StringBuilder a kimenethez:

int lastIndex = 0; StringBuilder kimenet = new StringBuilder (); Matcher matcher = TITLE_CASE_PATTERN.matcher (eredeti); while (matcher.find ()) {output.append (original, lastIndex, matcher.start ()) .append (convert (matcher.group (1))); lastIndex = matcher.end (); } if (lastIndex <eredeti.hossz ()) {output.append (eredeti, lastIndex, eredeti.hossz ()); } return output.toString ();

Meg kell jegyeznünk StringBuilder praktikus verzióját nyújtja mellékel amely képes kivonni az alszövegeket. Ez jól működik a vége tulajdona Matcher hogy az utolsó meccs óta felvegyük az összes nem egyeztetett karaktert.

4. Az algoritmus általánosítása

Most, hogy megoldottunk néhány speciális token cseréjét, miért nem konvertáljuk a kódot olyan formára, ahol az általános esetre használható? Az egyetlen dolog, amely megvalósításonként változik, az a használandó reguláris kifejezés, valamint az egyes mérkőzések cseréjévé alakításának logikája.

4.1. Használjon funkció- és mintabemenetet

Használhatunk Java-t Funkció objektum, hogy a hívó megadhassa az egyes mérkőzések feldolgozásának logikáját. És felvehetünk egy úgynevezett bemenetet tokenPattern hogy megtalálja az összes tokent:

// ugyanaz, mint korábban, míg (matcher.find ()) {output.append (original, lastIndex, matcher.start ()) .append (converter.apply (matcher)); // ugyanaz, mint korábban

Itt a reguláris kifejezés már nincs kódolva. Ehelyett a átalakító funkciót a hívó biztosítja, és a játék minden egyes mérkőzésére alkalmazza megtalálja hurok.

4.2. Az Általános verzió tesztelése

Nézzük meg, hogy az általános módszer ugyanúgy működik-e, mint az eredeti:

assertThat ("aizstTokens (" Először 3 nagybetűs szó! majd 10 TLA-t találtam ", TITLE_CASE_PATTERN, match -> match.group (1) .toLowerCase ())) .isEqualTo (" első 3 nagybetűs szó! majd 10 TLA, találtam ");

Itt látjuk, hogy a kód hívása egyszerű. A konverziós függvény könnyen kifejezhető lambdaként. És a teszt sikeres.

Most van egy token-helyettesítőnk, ezért próbálkozzunk más felhasználási esetekkel.

5. Néhány használati eset

5.1. Menekülés a különleges karakterek közül

Képzeljük el, hogy a reguláris kifejezés escape karakterét akartuk használni \ hogy a reguláris kifejezés minden karakterét manuálisan idézzük, ahelyett, hogy a idézet módszer. Lehet, hogy egy sztringet idézünk egy reguláris kifejezés létrehozásának részeként, hogy átadhassuk egy másik könyvtárnak vagy szolgáltatásnak, így a kifejezés blokk idézése nem lesz elég.

Ha ki tudjuk fejezni azt a mintát, amely „reguláris kifejezés karaktert” jelent, akkor algoritmusunkkal könnyű elkerülni mindet:

Pattern regexCharacters = Pattern.compile ("[]"); assertThat (substitTokens ("Egy olyan regex karakter, mint a [", regexCharacters, match -> "\" + match.group ())) .isEqualTo ("Egy olyan regex karakter, mint a \ [");

Minden meccshez előtagot adunk a \ karakter. Mint \ egy speciális karakter a Java karakterláncokban, mással elkerülve \.

Valóban, ezt a példát külön kitérjük \ karakterek, mint karakterosztály a mintában regexCharacters sok különleges karaktert kell idéznie. Ez azt mutatja, hogy a reguláris kifejezés értelmezője azt jelenti, hogy a literáljukra gondolunk, nem pedig a reguláris kifejezés szintaxisára.

5.2. Helyőrzők cseréje

A helyőrző kifejezésének általános módja a szintaxis használata $ {name}. Vegyünk egy felhasználási esetet, amikor a sablon "Szia $ {name}, $ {company}" nevű térképről kell feltölteni placeholderValues:

Map placeholderValues ​​= new HashMap (); placeholderValues.put ("név", "Bill"); placeholderValues.put ("vállalat", "Baeldung");

Csak egy jó reguláris kifejezésre van szükségünk hogy megtalálja a ${…} tokenek:

"\ $ \ {(? [A-Za-z0-9 -_] +)}"

az egyik lehetőség. Idéznie kell a $ és a kezdeti göndör zárójel, mivel különben reguláris kifejezés szintaxisként kezelnék őket.

Ennek a mintának a középpontjában a helyőrző nevét rögzítő csoport található. Olyan karakterosztályt használtunk, amely alfanumerikus, kötőjeles és aláhúzási karaktereket tesz lehetővé, aminek a legtöbb felhasználási esetnek meg kell felelnie.

Azonban, a kód olvashatóbbá tétele érdekében elneveztük ezt a rögzítő csoportothelykitöltő. Lássuk, hogyan kell használni a nevezett rögzítési csoportot:

assertThat (aizstTokens ("Szia $ {name}, $ {company}", "\ $ \ {(? [A-Za-z0-9 -_] +)}", match -> placeholderValues.get (match .group ("helyőrző")))) .isEqualTo ("Szia Bill Baeldungnál");

Itt láthatjuk, hogy a megnevezett csoport értékének kiszedése a Matcher csak magában foglalja a felhasználást csoport a név helyett a bemenet, nem pedig a szám.

6. Következtetés

Ebben a cikkben megvizsgáltuk, hogyan használhatunk erőteljes reguláris kifejezéseket a tokenek megtalálásához a karakterláncokban. Megtudtuk, hogyan megtalálja módszer működik Matcher hogy megmutassa nekünk a meccseket.

Ezután létrehoztunk és általánosítottunk egy algoritmust, amely lehetővé teszi számunkra, hogy tokenenként cseréljünk.

Végül megvizsgáltunk néhány gyakori esetet a karakterek elhagyására és a sablonok feltöltésére.

Mint mindig, a kód példák megtalálhatók a GitHub oldalon.