Regex, kort för regelbundet uttryck, används ofta i programmeringsspråk för matchning mönster i strängar, hitta och ersätta, mata in validering och omformatera text. Att lära sig att använda Regex på rätt sätt kan göra det mycket enklare att arbeta med text.
Regex-syntax, förklaras
Regex har rykte om att ha hemsk syntax, men det är mycket lättare att skriva än att läsa. Här är till exempel en allmän regex för en RFC 5322-kompatibel e-postvaliderare:
(?: [a-z0-9! # $% & amp; '* +/=? ^ _ `{|} ~ -] + (?: . [a-z0-9! # $% & amp;' * +/=? ^ _ `{|} ~ -] +) * |” (?: [ x01- x08 x0b x0c x0e- x1f x21 x23- x5b x5d- x7f] | \ [ x01- x09 x0b x0c x0e- x7f]) * “) @ (?: (?: [a-z0-9] (?: [a-z0-9 -] * [a -z0-9])? .) + [a-z0-9] (?: [a-z0-9 -] * [a-z0-9])? | [(? 🙁 ?: 25 [ 0-5] | 2 [0-4] [0-9] | [01]? [0-9] [0-9]?) .) {3} (?: 25 [0-5] | 2 [0-4] [0-9] | [01]? [0-9] [0-9]? | [A-z0-9 -] * [a-z0-9]: (?: [ X01 – x08 x0b x0c x0e- x1f x21- x5a x53- x7f] | \ [ x01- x09 x0b x0c x0e- x7f]) +) ])
Om det ser ut som om någon krossade ansiktet i tangentbordet är du inte ensam. Men under huven programmerar all denna röra faktiskt en finite-state-maskin. Den här maskinen körs för varje karaktär, passar och matchar baserat på regler som du har ställt in. Massor av onlineverktyg ger järnvägsdiagram som visar hur din Regex-maskin fungerar. Här är samma Regex i visuell form:
Fortfarande mycket förvirrande, men det är mycket mer förståeligt. Det är en maskin med rörliga delar som har regler som definierar hur allt passar ihop. Du kan se hur någon monterade detta; det är inte bara en stor textglob.
Först av: Använd en Regex-felsökare
Innan vi börjar, såvida inte din Regex är särskilt kort eller om du är särskilt skicklig , bör du använda en online-felsökare när du skriver och testar den. Det gör det lättare att förstå syntaxen. Vi rekommenderar Regex101 och RegExr, båda som erbjuder testning och inbyggd syntaxreferens.
Hur fungerar Regex?
Låt oss för närvarande fokusera på något mycket enklare. Detta är ett diagram från Regulex för en mycket kort (och definitivt inte RFC 5322-kompatibel) e-postmatchande Regex:
Regex-motorn startar till vänster och rör sig längs linjerna och matchar tecken när den går. Grupp # 1 matchar alla tecken utom ett radbrytning och fortsätter att matcha tecken tills nästa block hittar en matchning. I det här fallet slutar den när den når en @ -symbol, vilket innebär att grupp # 1 fångar e-postadressens namn och allt efter matchar domänen.
Regex som definierar grupp # 1 i vårt e-postexempel är:
(. +)
Parenteserna definierar en fångstgrupp, som säger till Regex-motorn att inkludera innehållet i gruppens matchning i en speciell variabel. När du kör en Regex på en sträng är standardreturen hela matchningen (i det här fallet hela e-postmeddelandet). Men det returnerar också varje fångstgrupp, vilket gör denna Regex användbar för att dra ut namn från e-postmeddelanden.
Perioden är symbolen för & # 8220; Alla tecken utom Newline. & # 8221; Detta matchar allt på en rad, så om du skickade detta e-postmeddelande Regex en adress som:
% $ # ^ & amp;% * #% $ # ^ @ gmail.com
Det skulle matcha% $ # ^ & amp ;% * #% $ # ^ Som namnet, även om det är löjligt.
Plussymbolen (+) är en kontrollstruktur som betyder att matcha föregående karaktär eller grupp en eller flera gånger. & # 8221; Det säkerställer att hela namnet matchas, och inte bara det första tecknet. Det här är det som skapar slingan som finns på järnvägsdiagrammet.
Resten av Regex är ganska enkel att dechiffrera:
(. +) @ (. + .. +)
Den första gruppen stannar när den träffar @ -symbolen. Nästa grupp startar sedan, som igen matchar flera tecken tills den når ett periodtecken.
Eftersom tecken som perioder, parenteser och snedstreck används som en del av syntaxen i Regrex, när du vill matcha de tecken du behöver för att undgå dem med ett snedstreck. I det här exemplet, för att matcha perioden vi skriver . och parsern behandlar den som en symbol som betyder & # 8220; matcha en period. & # 8221;
Teckenmatchning
Om du har icke-kontrolltecken i din Regex, Regex-motorn antar att dessa tecken kommer att bilda ett matchande block. Till exempel kommer Regex:
he + llo
Matcha ordet & # 8220; hej & # 8221; med valfritt antal e & # 8217; s. Alla andra tecken måste fly för att fungera ordentligt.
Regex har också karaktärsklasser, som fungerar som stenografi för en uppsättning karaktärer. Dessa kan variera beroende på Regex-implementeringen, men dessa få är standard:
- . & # 8211; matchar vad som helst utom newline.
- w & # 8211; matchar alla ord & # 8220; ord & # 8221; karaktär, inklusive siffror och understrykningar.
- d & # 8211; matchar siffror.
- b & # 8211; matchar mellanslagstecken (dvs. mellanslag, flik, ny rad).
Dessa tre har alla stora bokstäver som inverterar sin funktion. Till exempel matchar D allt som inte är ett nummer.
Regex har också matchning av teckenuppsättningar. Till exempel:
[abc]
Matchar antingen a, b eller c. Detta fungerar som ett block och hakparenteserna är bara kontrollstrukturer. Alternativt kan du ange ett teckenintervall:
[ac]
Eller negera uppsättningen, som matchar alla tecken som inte finns i uppsättningen:
[^ ac]
Kvantifierare
Kvantifierare är en viktig del av Regex. De låter dig matcha strängar där du inte vet det exakta formatet, men du har en ganska bra idé.
+ -operatören från e-postexemplet är en kvantifierare, specifikt den & # 8220; en eller mer & # 8221; kvantifierare. Om vi inte vet hur lång en viss sträng är, men vi vet att den består av alfanumeriska tecken (och inte är tom), kan vi skriva:
w +
Förutom + finns det också:
- * Operatören, som matchar & # 8220; noll eller mer. & # 8221; I princip samma som +, förutom att det har möjlighet att inte hitta en matchning.
- ? operatör, som matchar & # 8220; noll eller en. & # 8221; Det har effekten att göra en karaktär valfri; antingen är det där eller så är det inte, och det matchar inte mer än en gång.
- Numeriska kvantifierare. Dessa kan vara ett enda nummer som {3}, vilket betyder & # 8220; exakt tre gånger, & # 8221; eller ett intervall som {3-6}. Du kan utelämna det andra numret för att göra det obegränsat. Till exempel betyder {3,} & # 8220; tre eller flera gånger & # 8221 ;. Konstigt nog kan du inte utelämna det första numret, så om du vill ha & # 8220; tre eller mindre gånger, & # 8221; du måste använda ett intervall.
Giriga och lata kvantifierare
Under huven, * Och + operatörer är giriga. Det matchar så mycket som möjligt och ger tillbaka vad som behövs för att starta nästa block. Det här kan vara ett stort problem.
Här är ett exempel: säg att du försöker matcha HTML eller något annat med stängande hängslen. Din inmatade text är:
& lt; div & gt; Hello World & lt;/div & gt;
Och du vill matcha allt inom parentes. Du kan skriva något som:
& lt;. * & Gt;
Det här är rätt idé, men det misslyckas av en avgörande anledning: Regex-motorn matchar & # 8220; div & gt; Hello World & lt;/div & gt; & # 8221; för sekvensen. *, och sedan backtracks tills nästa block matchar, i det här fallet, ett stängningsfäste (& gt;). Du förväntar dig att det bara kommer att matcha & # 8220; div & # 8220 ;, och sedan upprepa igen för att matcha den avslutande div. Men backtracker går från slutet av strängen och kommer att stanna på slutfästet, vilket slutar matcha allt inuti fästena.
Lösningen är att göra vår kvantifierare lat, vilket innebär att den kommer att matcha som få tecken som möjligt. Under huven kommer detta faktiskt bara att matcha ett tecken och sedan expandera för att fylla utrymmet tills nästa blockmatchning, vilket gör det mycket mer prestanda i stora Regex-operationer.
Att göra en kvantifierare lat görs genom att lägga till ett frågetecken direkt efter kvantifieraren. Det här är lite förvirrande för? är redan en kvantifierare (och är faktiskt girig som standard). För vårt HTML-exempel fixas Regex med detta enkla tillägg:
& lt;. *? & Gt;
Den lata operatören kan anslutas till valfri kvantifierare, inklusive + ?, {0,3} ?, och till och med ??. Även om den sista inte har någon effekt; eftersom du ändå matchar noll eller ett tecken finns det inget utrymme att expandera.
Gruppering och Lookarounds
< p>Grupper i Regex har många syften. På grundnivå går de samman flera tokens i ett block. Du kan till exempel skapa en grupp och sedan använda en kvantifierare för hela gruppen:
ba (na) +
Denna grupperar det upprepade & # 8220; na & # 8221; för att matcha frasen banan och banananana och så vidare. Utan gruppen skulle Regex-motorn bara matcha slutkaraktären om och om igen.
Denna typ av grupp med två enkla parenteser kallas en fångstgrupp och kommer att inkludera den i utdata:
Om du vill undvika detta och helt enkelt gruppera tokens tillsammans av körskäl kan du använda en grupp som inte fångar:
ba (?: na)
Frågetecknet (ett reserverat tecken) definierar en icke-standardgrupp och följande tecken definierar vilken typ av grupp det är. Att starta grupper med ett frågetecken är perfekt, för annars om du vill matcha semikolon i en grupp behöver du inte fly från dem utan goda skäl. Men du måste alltid undvika frågetecken i Regex.
Du kan också namnge dina grupper när du arbetar med utdata:
(? 'Group')
Du kan referera dessa i din Regex, vilket gör att de fungerar som variabler. Du kan referera till icke-namngivna grupper med token 1, men detta går bara upp till 7, varefter du måste börja namnge grupper. Syntaxen för referens till namngivna grupper är:
k {group}
Detta refererar till resultaten från den namngivna gruppen, som kan vara dynamiska. I huvudsak kontrollerar den om gruppen förekommer flera gånger men bryr sig inte om positionen. Till exempel kan detta användas för att matcha all text mellan tre identiska ord:
Gruppklassen är där du hittar det mesta av Regex kontrollstruktur, inklusive lookaheads. Lookaheads ser till att ett uttryck måste matcha men inkluderar det inte i resultatet. På ett sätt liknar det ett if-uttalande och kommer inte att matcha om det returnerar falskt.
Syntaxen för en positiv lookahead är (? =). Här är ett exempel:
Detta matchar namndelen av en e-postadress mycket rent genom att stoppa körningen vid delningen @. Lookaheads konsumerar inte några tecken, så om du vill fortsätta springa efter att en lookahead lyckats kan du fortfarande matcha karaktären som används i lookahead.
Förutom positiva lookaheads finns det också:
- (?!) & # 8211; Negativa lookaheads, som säkerställer att ett uttryck inte matchar.
- (? & lt; =) & # 8211; Positivt utseende, som inte stöds överallt på grund av vissa tekniska begränsningar. Dessa placeras framför uttrycket du vill matcha och de måste ha en fast bredd (dvs. inga kvantifierare utom {nummer}. I det här exemplet kan du använda (? & Lt; = @) w + . W + för att matcha domändelen av e-postmeddelandet.
- (? & lt ;!) & # 8211; Negativa lookbehinds, som är samma som positiva lookbehinds, men negerade.
Skillnader mellan Regex-motorer
Inte alla Regex skapas lika. De flesta Regex-motorer följer inte någon specifik standard, och vissa byter upp saker för att passa deras språk. Vissa funktioner som fungerar på ett språk kanske inte fungerar på ett annat.
Till exempel stöder inte versionerna av sed som samlats för macOS och FreeBSD att använda t för att representera ett fliktecken. Du måste kopiera ett tabbtecken manuellt och klistra in det i terminalen för att använda en flik i kommandoraden sed.
Det mesta av denna handledning är kompatibel med PCRE, den vanliga Regex-motorn som används för PHP. Men JavaScript: s Regex-motor är annorlunda, den stöder inte namngivna gruppgrupper med citattecken (den vill ha parenteser) och kan inte göra rekursion bland annat. Även PCRE är inte helt kompatibelt med olika versioner, och det har många skillnader från Perl regex.
Det finns för många mindre skillnader att lista här, så du kan använda denna referensbord för att jämföra skillnaderna mellan flera Regex-motorer. Regex-felsökare som Regex101 låter dig också byta Regex-motorer, så se till att du felsöker med rätt motor.
Så här kör du Regex
Vi har diskuterat den matchande delen av reguljära uttryck, som utgör det mesta av det som gör en Regex. Men när du faktiskt vill köra din Regex, måste du forma den till ett fullständigt reguljärt uttryck.
Detta tar vanligtvis formatet:
/match/g
Allt inuti framåt snedstreck är vår match. G är en modifierare. I det här fallet säger den att motorn inte ska sluta gå efter att den hittat den första matchen. För att hitta och ersätta Regex måste du ofta formatera det som:
/hitta/ersätta/g
Detta ersätter hela filen. Du kan använda fånga gruppreferenser när du byter ut, vilket gör Regex mycket bra på att formatera text. Till exempel kommer denna Regex att matcha alla HTML-taggar och ersätta standardfästena med hakparenteser:
/<(.+?)>/[\]/g
När detta går kommer motorn att matcha & lt; div & gt; och & lt;/div & gt;, så att du kan ersätta denna text (och endast denna text). Som du kan se påverkas inte den inre HTML:
Detta gör Regex mycket användbart för att hitta och ersätta text. Kommandoradsverktyget för att göra detta är sed, som använder det grundläggande formatet för:
sed '/hitta/ersätta/g' fil & gt; fil
Den här körs på en fil och matas ut till STDOUT. Du måste pipa den till sig själv (som visas här) för att faktiskt ersätta filen på disken.
Regex stöds också i många textredigerare och kan verkligen påskynda ditt arbetsflöde när du gör batch operationer. Vim, Atom och VS Code har alla inbyggda Regex-sökningar och ersättare.
Naturligtvis kan Regex också användas programmatiskt och är vanligtvis inbyggt på många språk. Den exakta implementeringen beror på språket, så du måste konsultera ditt språk dokumentation.
Till exempel, i JavaScript kan regex skapas bokstavligen eller dynamiskt med den globala RegExp objekt:
var re = new RegExp ('abc')
Detta kan användas direkt genom att anropa .exec () -metoden för det nyligen skapade regex-objektet, eller genom att använda .replace (), .match () och .matchAll () -metoder på strängar.