JavaScript Regular Expressions Utöver grunderna

I vår tidigare handledning om regelbundna uttryck i JavaScript lärde du dig om användbarheten av vanliga uttryck och hur du skriver en del av dina egna för att matcha enkla mönster.

Efter att ha läst den tidigare handledningen bör du nu ha en bra förståelse av specialtecken som en backslash och karaktärsekvenser som \ w eller \ W. Här är en riktigt snabb sammanfattning av de teckensekvenserna:

  1. Du kan använda \ d eller \ D att matcha en siffra eller en icke-siffrig bokstav i en given sträng. Siffror inkluderar 0, 1, 2, 3, 4, 5, 6, 7, 8 och 9. Alla andra tecken kommer att matchas av \ D.
  2. Du kan använda \ w eller \ W för att matcha ett ord eller icke-ordtecken i en viss sträng. Ordtecken innehåller alfabet, siffror och understreck. Allt annat, som,,%, etc., anses vara ett icke-ord-tecken.
  3. Du kan använda \ s eller \ S för att matcha mellanslagstecken eller tecken utan mellanslag i en sträng. Mellanslagstegn inkluderar utrymme, flik, formulärmatning och radmatning.

I stället för att matcha ett tecken åt gången kan du använda * symbol för att matcha föregående uttryck noll eller flera gånger. De + tecken kommer på samma sätt matcha föregående uttryck 1 eller flera gånger.

Du kan matcha ett mönster ett visst antal gånger genom att lägga till n, m till det. Här, n är det lägsta antalet gånger du vill matcha det, och m är gränsvärdet. Om du inte anger ett värde för m, föregående uttryck matchas så många gånger som möjligt.

Du borde kolla in min tidigare handledning om något som vi bara täckt inte är klart. Jag förklarade allt mer detaljerat där.

Låt oss nu gå vidare till några mer sofistikerade teckensekvenser i reguljära uttryck, så att du kan få ut det mesta av dem och ta reda på hur du skriver uttryck som matchar komplicerade mönster.

Non-Greedy Matches Använda ? Karaktär

De ? karaktär betyder olika saker i olika situationer.

När den används ensam matchar det här tecknet uttrycket som kom före det 0 eller 1 gånger. I den meningen är det samma som 0,1.

Du kan också använda ? omedelbart efter andra kvantifierare som *, + och för att matcha det lägsta möjliga antalet tecken. Med andra ord kommer det att göra dessa giriga kvantifierare till icke-giriga. Det kan vara lite svårt att förstå utan att titta på levande exempel, så låt oss se ett exempel först.

Tänk på följande mening:

Jag har tilldelats 17321HDGE som användar-ID medan min vän tilldelades FHES193EK1.

Låt oss nu se alla matcher som skulle ha returnerats av olika kvantifierare och deras icke-giriga motsvarighet.

Om vi ​​använder uttrycket / \ D + / g i exemplet kommer det att matcha en eller flera på varandra följande siffertecken. På grund av den globala flaggan kommer det att finnas tre matchningar: 17321, 193, och 1.

Du bör notera det 193 och 1 anses vara olika matchningar eftersom de är separerade av EK.

Följande exempel visar matcherna utan att någon kvantifierare används.

var re = / \ d + / g; var räknat = 0; var textString = "Jag har tilldelats 17321HDGE som användarnamn medan min vän tilldelades FHES193EK1."; var match = re.exec (textString); medan (match! == null) console.log (match [0]); match = re.exec (textString); räkna ++;  console.log ("Totalt matchningar:" + räknat); / * Utgång 17321 193 1 Totalt antal matchningar: 3 * /

Nu lägger du till en ? karaktär efter \ d+ kommer att returnera nio olika matchningar. I grund och botten, / \ D +? / kommer att vända varje siffertecken till en separat matchning. Varför är det så?

Det är på grund av \ d+ är enligt definition tänkt att matcha en eller flera siffror. Sedan ? karaktären ska matcha det lägsta möjliga antalet tecken, det matchar bara en enda siffra åt gången.

Den icke-giriga ? kvantifieraren returnerar 9 mindre enkelsiffriga matcher denna gång. För korthet har jag kommenterat linjen som loggar matcherna till konsolen.

var re = / \ d + a / g; var räknat = 0; var textString = "Jag har tilldelats 17321HDGE som användarnamn medan min vän tilldelades FHES193EK1."; var match = re.exec (textString); medan (match! == null) // console.log (matcha [0]); match = re.exec (textString); räkna ++;  console.log ("Totalt matchningar:" + räknat); / * Totalt antal matcher: 9 * /

Låt oss ta ett annat exempel. Det reguljära uttrycket / \ W + / kommer att behålla matchande ordtecken så länge de inte avbryts av ett icke-ordtyp som rymden. I vårt fall kommer det att matcha hela rymdseparerade ord som delad och 17321HDGE en gång till.

Om vi ​​ersätter vårt ursprungliga reguljära uttryck med / \ W + /, vi får 14 olika matcher. I grund och botten kommer varje ord att vara en egen match. Du kan se produktionen själv genom att kommentera raden.

var re = / \ w + / g; var räknat = 0; var textString = "Jag har tilldelats 17321HDGE som användarnamn medan min vän tilldelades FHES193EK1."; var match = re.exec (textString); medan (match! == null) // console.log (matcha [0]); match = re.exec (textString); räkna ++;  console.log ("Totalt matchningar:" + räknat); / * Totalt antal matcher: 14 * /

Nu ändrar du uttrycket till / \ W +? / kommer att returnera varje ordtecken som en separat match, och du får 68 matchningar.

Låt oss ta en titt på ett sista exempel innan vi fortsätter vidare. Det reguljära uttrycket / \ W 4, / kommer att returnera alla ord i vår mening som är fyra tecken eller längre. Så det matchar ha, varit, delad, och 17321HDGE, bland andra. Nu vänder den till / \ W 4,? / skulle returnera flera matchningar från ord med mer än fyra tecken. I vårt exempel skulle de återkomna matcherna vara ha, varit, assi, gned, 1732, och 1HGD. Karaktären E i slutet av 17321HDGE ingår inte i någon matcheftersom det inte kunde vara i gruppen av fyra på varandra följande ordtecken.

var re = / \ w 4, / g; var räknat = 0; var textString = "Jag har tilldelats 17321HDGE som användarnamn medan min vän tilldelades FHES193EK1."; var match = re.exec (textString); medan (match! == null) console.log (match [0]); match = re.exec (textString); räkna ++;  console.log ("Totalt matchningar:" + räknat); / * Output har tilldelats 17321HDGE-användare medan vän tilldelats FHES193EK1 Totalt matchningar: 9 * /

Använda parenteser med? Karaktär

I min tidigare regex-handledning behandlade jag kortfattat hur parenteser kan användas för att komma ihåg en del av en match. När den används med a ? karaktär, de kan också tjäna andra syften.

Ibland vill du ha en grupp tecken som matchar som en enhet. Till exempel kan du leta efter förekomsten av na en eller två gånger som en match i följande text.

na naa nnaa nana naana

För att förtydliga, letar du efter den djärva texten som matchningar: na naennaen (Nana) naenna. Delen i parentesen ska matchas som en enhet, så det räknas bara som en match.

Nästan alla som bara börjar med regex kommer att använda uttrycket / Na 1,2 / med avsikt att få det förväntade resultatet. I deras sinne, den 1,2 del är tänkt att matcha en eller två förekomster av n och en tillsammans. Emellertid matchar det faktiskt en enda förekomst av n följt av 1 eller 2 händelser av karaktären en.

Jag har gjort matcherna tillbaka av / Na 1,2 / med fetstil för förtydligande: na naa nnaa (Na) (na) (Naa) (na). Delarna i parenteserna är separata matchningar. Som du kan se får vi inte det resultat vi ville ha eftersom 1,2 överväger inte na att vara en enda enhet som måste matchas.

Lösningen här är att använda parentes för att berätta för JavaScript att matcha na som en enhet. Men som vi såg i den tidigare handledningen börjar JavaScript att komma ihåg matchen på grund av parenteserna.

Om du inte vill att JavaScript ska komma ihåg matchen måste du lägga till ?: före den grupp av tecken som du försöker matcha. I vårt fall skulle det sista uttrycket bli / (?: na) 1,2 /. Gruppen na kommer att matchas som en enhet nu, och det kommer inte att komma ihåg. Jag har markerat de sista matcherna som returneras med detta uttryck i fetstil: na naennaa (nana) naenna.

Följande exempel loggar alla matcher till konsolen. Eftersom det finns totalt 6 matcher är det totala matchantalet 6.

var re = / (?: na) 1,2 / g; var räknat = 0; var textString = "na naa nnaa nana naana"; var match = re.exec (textString); medan (match! == null) console.log (match [0]); match = re.exec (textString); räkna ++;  console.log ("Totalt matchningar:" + räknat); / * Utgående resultat efter totalt antal matcher: 6 * /

Lookahead och Negated Lookahead

Det finns många situationer där vi letar efter att matcha en given uppsättning tecken, men endast om de är eller inte följs av en annan uppsättning tecken. Till exempel kan du leta efter ordet äpplen i en text men vill bara ha de matchningar som följs av är. Tänk på följande mening.

äpplen är smaskiga. Vi åt äpplen hela dagen. Alla som åt äpplen tyckte om dem.

I ovanstående exempel vill vi bara det första ordet som en match. Varannan förekomst av ordet borde inte vara i matcherna.

Ett sätt att uppnå detta är att använda följande reguljära uttryck en (? = b). Ordet vi vill matcha är en, och ordet som ska komma efter en är b. I vårt fall skulle uttrycket bli / äpplen (? = \ Sare) /. Kom ihåg att ordet är ingår inte i den här matchen.

var re = / äpplen (? = \ sare) / g; var räknat = 0; var textString = "äpplen är smaskiga. Vi åt äpplen hela dagen. Alla som åt äpplen tyckte om dem."; var match = re.exec (textString); medan (match! == null) console.log (match [0]); match = re.exec (textString); räkna ++;  console.log ("Totalt matchningar:" + räknat); / * Utgångsäpplar Totalt antal matcher: 1 * /

Detta regelbundna uttryck, där vi tittar på vad som kommer i strängen innan vi bestämmer om ordet är en match, kallas en lookahead.

En mycket liknande situation skulle uppstå om du bestämde dig för att matcha äpplen bara om det var inte följt av en specifik uppsättning tecken. I sådana fall måste du byta ut ?= med ?! i ditt ordinarie uttryck. Om vi ​​letade efter alla händelser av äpplen vilka är inte följd av är, vi kommer använda / äpplen (?! \ Sare) / som vårt vanliga uttryck. Det kommer att finnas två framgångsrika matcher för vår testföljd.

var re = / äpplen (?! \ sare) / g; var räknat = 0; var textString = "äpplen är smaskiga. Vi åt äpplen hela dagen. Alla som åt äpplen tyckte om dem."; var match = re.exec (textString); medan (match! == null) console.log (match [0]); match = re.exec (textString); räkna ++;  console.log ("Totalt matchningar:" + räknat); / * Utgång äpplen äpplen Totalt antal matcher: 2 * /

En sak - du behöver inte använda två separata reguljära uttryck för att hitta alla matchningar som följs av någon av två givna ord. Allt du behöver göra är att lägga till röroperatören mellan dessa ord och du är bra att gå. Till exempel, om du letar efter alla förekomster av äpple som följs av är eller var, du borde använda / äpplen (\ sare |?! \ swere) / som ditt vanliga uttryck.

Slutgiltiga tankar

I denna handledning lärde vi oss att skriva komplicerade reguljära uttryck för att matcha de mönster vi letar efter. Vi kan använda specialen ? tecken för att returnera det minsta önskade antalet av föregående tecken som en matchning. På samma sätt kan vi använda ? inom parentes för att se till att gruppen vi matchade inte kommer ihåg. 

Slutligen lärde vi oss att ?= och ?! tecken sekvenser i ett regelbundet uttryck ger oss möjlighet att returnera en viss uppsättning tecken som en match endast om de är eller följs av en annan given uppsättning tecken.

Om du har några frågor relaterade till denna handledning, var god och låt mig veta och jag kommer att göra mitt bästa för att förklara dem.