Regelbundna uttryck (AKA regex) är ett formellt språk som definierar en sekvens av tecken med lite mönster. I den verkliga världen kan de användas för att lösa många problem med halvstrukturerad text. Du kan extrahera viktiga bitar och bitar från text med många dekorationer eller orelaterat innehåll. Go har ett starkt regex-paket i sitt standardbibliotek som låter dig skiva och tärningstext med regexes.
I den här tvådelade serien lär du dig vilka vanliga uttryck som är och hur du använder regelbundna uttryck effektivt i Go för att utföra många vanliga uppgifter. Om du inte är bekant med regelbundna uttryck alls finns det massor av bra handledning. Här är en bra.
Låt oss börja med ett snabbt exempel. Du har lite text, och du vill kontrollera om den innehåller en e-postadress. En e-postadress anges strängt i RFC 822. Kort sagt, den har en lokal del följt av en @ symbol följt av en domän. E-postadressen separeras från resten av texten med plats.
För att se om det innehåller en e-postadress kommer följande regex att göra: ^ \ W + @ \ w + \. \ W + $
. Observera att denna regex är lite tillåten och tillåter några ogiltiga e-postadresser genom. Men det är tillräckligt bra att visa konceptet. Låt oss försöka på ett par potentiella e-postadresser innan vi förklarar hur det fungerar:
paketet huvudimport ("os" "regexp" "fmt") func check (fel fel) om err! = nil fmt.Println (err.Error ()) os.Exit (1) func main e-postmeddelanden: = [] sträng "brun @ räv", "brun @ räv", "[email protected]", "br @ own @ fox.com", mönster: = '^ \ w + @ \ w + \ . \ w + $ 'för _, email: = intervall emails matched, err: = regexp.Match (mönster, [] byte (email)) check (err) om matchad fmt.Printf ("√'% s 'är ett giltigt e-postmeddelande \ n ", e-post) annat fmt.Printf (" X "% s" är inte ett giltigt e-postmeddelande \ n ", email) Output: X 'brown @ Fox' är inte ett giltigt e- 'brun @ räv.' är inte ett giltigt email √ '[email protected]' är ett giltigt e-postmeddelande X 'br @ own @ fox.com' är inte ett giltigt e-postmeddelande
Vårt vanliga uttryck fungerar på det här lilla provet. De två första adresserna avvisades eftersom domänen inte hade en punkt eller inte hade några tecken efter pricken. Den tredje e-posten formaterades korrekt. Den sista kandidaten hade två @ symboler.
Låt oss bryta den här regexen ner: ^ \ W + @ \ w + \. \ W + $
Tecken / symbol | Menande |
---|---|
^ | Början av måltexten |
\ w | Några ordet tecken [0-9A-Za-z_] |
+ | Minst en av de föregående tecknen |
@ | Bokstavligen @ -tecknet |
\. | Den bokstavliga pricktecknet. Måste bli rymd med \ |
$ | Slut på måltexten |
Sammantaget kommer denna regex att matcha textstycken som börjar med ett eller flera ordtecken, följt av "@" -tecknet, följt igen av ett eller flera ordtecken, följt av en punkt och följt av en gång till ett eller flera ordtecken.
Följande tecken har speciella betydelser i reguljära uttryck: .+? * () | [] ^ $ \
. Vi har redan sett många av dem i e-postexemplet. Om vi vill matcha dem bokstavligen, måste vi fly dem med en backslash. Låt oss introducera en liten hjälparfunktion som heter match()
Det kommer att rädda oss mycket att skriva. Det tar ett mönster och lite text, använder regexp.Match ()
Metod för att matcha mönstret till texten (efter omvandling av texten till en byte array) och skriv ut resultaten:
func match (mönstersträng, textsträng) matched, _: = regexp.Match (mönster, [] byte (text)) om matchad fmt.Println ("√", mönster, ":", text) else fmt.Println ("X", mönster, ":", text)
Här är ett exempel på att matcha en vanlig karaktär som z
vs. matcha ett specialtecken som ?
:
func main () text: = "Kan jag haz cheezburger?" mönster: = "z" match (mönster, text) mönster = "\\?" matcha (mönster, text) mönster = '\?' matcha (mönster, text) Utgång: √ z: Kan jag få cheezburger? √ \? : Kan jag få cheezburger? √ \? : Kan jag få cheezburger?
Regexmönstret \?
innehåller en backslash som måste släppas med en annan backslash när den representeras som en vanlig Go-sträng. Anledningen är att backslash också används för att flytta specialtecken i Go strängar som newline (\ n
). Om du vill matcha självkopplingsperspektivet själv behöver du fyra snedstreck!
Lösningen är att använda Go raw strings med backtick ('
) istället för dubbla citat. Självklart, om du vill matcha den nya karaktären, måste du gå tillbaka till vanliga strängar och hantera flera backslash-utrymningar.
I de flesta fall försöker du inte att matcha en sekvens av specifika tecken som "abc", men en sekvens av okänd längd med kanske några kända tecken som injiceras någonstans. Regexes stöder detta användarfall med pricken .
speciell karaktär som står för vilken som helst karaktär. De *
specialtecken upprepar föregående tecken (eller grupp) noll eller flera gånger. Om du kombinerar dem, som i .*
, då matchar du något eftersom det helt enkelt betyder noll eller flera tecken. De +
liknar mycket *
, men det matchar en eller flera av de föregående tecknen eller grupperna. Så .+
kommer att matcha alla icke-tomma texter.
Det finns tre typer av gränser: början på texten betecknad av ^
, slutet på texten betecknad av $
, och ordet gränsen betecknas av \ b
. Tänk på att den här texten från den klassiska filmen ska beaktas Prinsessan Bruden: "Jag heter Inigo Montoya. Du dödade min far. Förbered dig att dö." Om du matchar bara "pappa" får du en match, men om du letar efter "far" i slutet av texten måste du lägga till $
karaktär, och då kommer det inte att finnas någon matchning. Å andra sidan fungerar matchande "Hello" i början bra.
func main () text: = "Hej jag heter Inigo Montoya, du dödade min pappa och förberedde mig för att dö." mönster: = "fader" match (mönster, text) mönster = "pappa $" match (mönster, text) mönster = "^ hej" matcha (mönster, text) Utgång: √ far: Hej jag heter Inigo Montoya, Du dödade min far, förbered dig för att dö. X pappa $: Hej jag heter Inigo Montoya, du dödade min far, förbereder dig för att dö. √ ^ Hej: Hej jag heter Inigo Montoya, du dödade min pappa och förberedde dig för att dö.
Ordgränser ser på varje ord. Du kan starta och / eller avsluta ett mönster med \ b
. Observera att skiljetecken som kommatecken anses vara en gräns och inte del av ordet. Här är några exempel:
func main () text: = 'Hej, jag heter Inigo Montoya, du dödade min far, förbereder dig för att dö.' mönster: = 'kill' match (mönster, text) mönster = '\ bkill' match (mönster, text) mönster = 'kill \ b' match (mönster, text) mönster = '\ bkill \ b' match ) mönster = '\ bkilled \ b' match (mönster, text) mönster = '\ bMontoya, \ b' match (mönster, text) Utgång: √ döda: Hej jag heter Inigo Montoya, du dödade min pappa, förberedde att dö. √ \ bkill: Hej jag heter Inigo Montoya, du dödade min far, förbereder dig för att dö. X dödar \ b: Hej jag heter Inigo Montoya, du dödade min far, förbereder dig för att dö. X \ bkill \ b: Hej, jag heter Inigo Montoya, du dödade min far, förbereder dig för att dö. √ \ bkilled \ b: Hej jag heter Inigo Montoya, du dödade min far, förbereder dig för att dö. X \ bMontoya, \ b: Hej, jag heter Inigo Montoya, du dödade min far, förbereder dig för att dö.
Det är ofta användbart att behandla alla grupper av tecken tillsammans som alla siffror, mellanslagstegn eller alla alfanumeriska tecken. Golang stöder POSIX-klasserna, vilka är:
Teckenklass | Menande |
---|---|
[: Alnum:] | alfanumerisk (≡ [0-9A-Za-z]) |
[:alfa:] | alfabetisk (≡ [A-Za-z]) |
[: Ascii:] | ASCII (≡ [\ x00- \ x7F]) |
[:tom:] | tomt (≡ [\ t]) |
[: Cntrl:] | kontroll (≡ [\ x00- \ x1F \ x7F]) |
[:siffra:] | siffror (≡ [0-9]) |
[:Graf:] | grafisk (≡ [! - ~] == [A-Za-z0-9! "# $% & '() * +, \ -. / :;<=>?@ [\\\] ^ _ '| ~]) |
[:lägre:] | små bokstäver (≡ [a-z]) |
[:skriva ut:] | utskrivbar (≡ [- ~] == [[: graph:]]) |
[: Punct:] | skiljetecken (≡ [! - /: - @ [- '- ~]) |
[:rymden:] | whitespace (≡ [\ t \ n \ v \ f \ r]) |
[:övre:] | stor bokstav (≡ [A-Z]) |
[:ord:] | ordtecken (≡ [0-9A-Za-z_]) |
[: Xdigit:] | hex-siffran (≡ [0-9A-Fa-f]) |
I följande exempel använder jag [:siffra:]
klass för att leta efter siffror i texten. Dessutom visar jag här hur man söker efter ett exakt antal tecken genom att lägga till det begärda numret i lockade axlar.
func main () text: = 'Svaret på livet, universum och allt är 42. "Mönster: =" [[: cif:]] 3 "matcha (mönster, text) mönster =" [ ]] 2 "matcha (mönster, text) Utgång: X [[: siffra:]] 3: Svaret på livet, universum och allt är 42. √ [[: ciffer:]] 2: Svaret på livet, universum och allt är 42.
Du kan också definiera dina egna klasser genom att sätta tecken i kvadratkonsoler. Om du till exempel vill kontrollera om någon text är en giltig DNA-sekvens som bara innehåller tecknen ACGT
använd sedan ^ [ACGT] * $
regex:
func main () text: = "AGGCGTTGGGAACGTT" mönster: = "^ [ACGT] * $" match (mönster, text) text = "Inte exakt en DNA-sekvens" matchning (mönster, text) Utgång: √ ^ [ACGT ] * $: AGGCGTTGGGAACGTT X ^ [ACGT] * $: Inte exakt en DNA-sekvens
I vissa fall finns det flera användbara alternativ. Matchande HTTP-webbadresser kan präglas av ett protokollschema, vilket antingen är http: //
eller https: //
. Rörkaraktären |
kan du välja mellan alternativ. Här är en regex som kommer att sortera dem ut: (Http) | (https):. // \ w + \ \ w 2,
. Det översätts till en sträng som börjar med http: //
eller https: //
följt av åtminstone ett ordtecken följt av en punkt följt av åtminstone två ordtecken.
func main () mönster: = '(http) | (https): // \ w + \. \ w 2, "match (mönster," http://tutsplus.com ") match : //tutsplus.com ") matchning (mönster," htt: //tutsplus.com ") Utgång: √ (http) | (https): // \ w + \. \ w 2,: http: /tutsplus.com √ (http) | (https): // \ w + \. \ w 2,: https://tutsplus.com X (http) | (https): // \ w + \. \ w 2,: htt: //tutsplus.com
I den här delen av handledningen täckte vi en hel del grund och lärde oss mycket om reguljära uttryck, med praktiska exempel med Golang regexp-biblioteket. Vi fokuserade på ren matchning och hur vi uttrycker våra intentioner med regelbundna uttryck.
I del två kommer vi att fokusera på att använda reguljära uttryck för att arbeta med text, inklusive fuzzy hitta, ersätta, gruppera och hantera nya linjer.