Regelbundna uttryck med go Del 2

Översikt

Detta är del två i en tvådelad serie av handledning om regelbundna uttryck i Go. I del ett lärde vi oss vad vanliga uttryck är, hur man uttrycker dem i Go och grunderna för att använda Go regexp-biblioteket för att matcha text mot vanliga uttrycksmönster. 

I del två kommer vi att fokusera på att använda regexp biblioteket i sin fulla utsträckning, bland annat sammanställa vanliga uttryck, hitta en eller flera matchningar i texten, ersätta reguljära uttryck, gruppera submatcher och hantera nya linjer.

Använda Regexp Library

Regexp-biblioteket ger fullfjädrad support för reguljära uttryck samt förmågan att kompilera dina mönster för effektivare utförande när du använder samma mönster för att matcha flera texter. Du kan också hitta index över träffar, ersätta matchningar och använda grupper. Låt oss dyka in.

Kompilera din Regex

Det finns två metoder för att sammanställa regexes: Sammanställa() och MustCompile (). Sammanställa() kommer att returnera ett fel om det angivna mönstret är ogiltigt. MustCompile () kommer att få panik Kompilering rekommenderas om du bryr dig om prestanda och planerar att använda samma regex flera gånger. Låt oss ändra vår match() hjälparfunktion för att ta en sammanställd regex. Observera att det inte finns något behov av att söka efter fel eftersom den sammanställda regexen måste vara giltig.

func match (r * regexp.Regexp, textsträng) matchad: = r.MatchString (text) om matchad fmt.Println ("√", r.String (), ":", text) annars fmt. Println ("X", r.String (), ":", text) 

Så här sammanställer du och använder samma sammanställda regex flera gånger:

func main () es: = '(\ bcats? \ b) | (\ brats? \ b)' e: = regexp.MustCompile (es) match och katter ") matcha (e," Katalogen är klar. Det är hotdog tid! ") match (e," Det är en hund äta hund värld. ") Output: √ (\ bcats? \ b) | \ b) | (\ brats? \ b): Det regnar hundar och katter X (\ bcats? \ b) | (\ bdogs? \ b) | (\ brats? \ b): Katalogen är klar. Det är hotdog tid! √ (\ bcats? \ B) | (\ bdogs? \ B) | (\ brats? \ B): Det är en hund som äter hundvärlden. 

Upptäckt

Regexp-objektet har a massa av FindXXX () metoder. Några av dem återvänder den första matchen, andra återvänder alla matcher, men andra återvänder ett index eller index. Intressant nog matchar namnen på alla 16 funktionsmetoder följande regex: Hitta (Alla)? (String)? (Submatch)? (Index)?

Om "Alla" är närvarande, returneras alla matchningar mot den vänstra sidan. Om 'String' är närvarande är måltexten och returvärdena strängar vs byte-arrays. Om 'Submatch' är närvarande returneras submatcher (grupper) vs bara enkla matchningar. Om 'Index' är närvarande returneras index inom måltexten jämfört med de aktuella matchningarna.

Låt oss ta en av de mer komplicerade funktionerna till uppgift och använda FindAllStringSubmatch () metod. Det tar en sträng och ett tal n. Om n är -1, kommer det att returnera alla matchande index. Om n är ett icke-negativt heltal kommer det att returnera de n vänstra matcherna. Resultatet är en skiva av strängskivor. 

Resultatet av varje submatch är den fulla matchningen följt av den fångade gruppen. Tänk på en lista över namn där några av dem har titlar som "Mr", "Mrs." eller "Dr.". Här är en regex som fångar titeln som en submatch och sedan resten av namnet efter ett mellanslag: \ b (Mr \. | Mrs \. | Dr \.). *.

func main () re: = regexp.MustCompile ('\ b (Mr \. | Mrs \. | Dr \.). *') fmt.Println (re.FindAllStringSubmatch ("Dr. Dolittle", -1)) fmt.Println (re.FindAllStringSubmatch ('Mrs. Doubtfire Mr. Anderson', -1)) Utgång: [[Dr. Dolittle Dr.]] [[Mrs. Doubtfire Mrs.] [Herr Anderson Herr]] 

Som du kan se i utmatningen är hela matchen först och sedan bara titeln. För varje rad återställs sökningen.

Byter ut

Att hitta matcher är bra, men ofta kan du behöva byta matchen med något annat. Regexp-objektet har flera ReplaceXXX () metoder som vanligt för att hantera strängar vs byte arrays och bokstavliga ersättningar vs. expansions. I den stora boken 1984 av George Orwell är partiets slagord inskriven på sanningens vita pyramid: 

  • Krig är fred 
  • Frihet är slaveri 
  • Ignorans är styrka 

Jag hittade en liten uppsats om pris av frihet som använder några av dessa villkor. Låt oss rätta ett stycke av det enligt partiet doublespeak med Go regexes. Observera att några av målorden för ersättning använder olika kapitaliseringar. Lösningen är att lägga till den otillräckliga flaggan (jag?) i början av regexen. 

Eftersom översättningen är annorlunda beroende på fallet behöver vi en mer sofistikerad metod än bokstavlig ersättning. Lyckligtvis (eller genom design) har Regexp-objektet en ersättningsmetod som accepterar en funktion som den använder för att utföra den faktiska ersättningen. Låt oss definiera vår ersättningsfunktion som returnerar översättningen med rätt fall.

sträng (sträng) sträng d: = map [sträng] sträng "krig": "fred", "WAR": "FRED", "Krig": "Fred", "frihet" FREEDOM ":" SLAVERY "," Freedom ":" Slaveri "," okunnighet ":" styrka "," IGNORANCE ":" STRENGTH "," Okunnighet ":" Styrka ", r, ok: = d [s] om ok return r else return s 

Nu kan vi utföra den faktiska ersättningen:

func main () text: = 'Frihetens pris: Amerikaner i krig Amerikanerna har gått i krig för att vinna sitt oberoende, utöka sina nationella gränser, definiera sina friheter och försvara sina intressen runt om i världen. expr: = '(? i) (krig | frihet | okunnighet)' r: = regexp.MustCompile (expr) resultat: = r.ReplaceAllStringFunc (text, ersättare) fmt.Println (resultat) Output: SLAVERY PRIS: Amerikaner i fred amerikanerna har gått till fred för att vinna sitt oberoende, utöka sina nationella gränser, definiera sina slaverier och försvara sina intressen runt om i världen. 

Produktionen är något osammanhängande, vilket är kännetecknet för god propaganda.

gruppering

Vi såg hur man använder gruppering med submatcher tidigare. Men det är ibland svårt att hantera flera submatcher. Namngivna grupper kan hjälpa mycket här. Så här namnger du dina submatchgrupper och fyller i en ordlista för enkel åtkomst med namn:

func main () e: = '(? P\ w +) (? P.+ )? (? P\ w +) 'r: = regexp.MustCompile (e) namn: = r.SubexpNames () fullNames: = [] sträng ' John F. Kennedy ',' Michael Jordan ' för _, fullName: = range fullNames result : = r.FindAllStringSubmatch (fullName, -1) m: = map [string] string  för i, n: = resultat för rad [0] m [namn [i]] = n fmt.Println : ", m [" först "]) fmt.Println (" middle_name: ", m [" middle "]) fmt.Println (" efternamn: ", m [" sista "]) fmt.Println Utgång: Förnamn: John medelnamn: F. Efternamn: Kennedy Förnamn: Michael med.namn: Efternamn: Jordan

Hantera nya linjer

Om du kommer ihåg sa jag att prickens specialtecken matchar alla tecken. Jo, jag ljög. Det matchar inte newlineen (\ n) tecken som standard. Det betyder att dina matchningar inte kommer att korsa rader om du inte specificerar det explicit med specialflaggan (? S) som du kan lägga till i början av din regex. Här är ett exempel med och utan flaggan.

func main () text: = "1111 \ n2222" expr: = [] sträng ". *", "(? s). *" för _, e: = intervall expr r: = regexp.MustCompile e) resultat: = r.FindString (text) result = strings.Replace (resultat, "\ n", '\ n', -1) fmt.Println (e, ":", resultat) fmt.Println ()  Utmatning:. *: 1111 (s). *: 1111 \ n2222 

En annan övervägning är huruvida man ska behandla ^ och $ specialtecken som början och slutet på hela texten (standard) eller som början och slutet av varje rad med (? M) flagga.  

Slutsats

Regelbundna uttryck är ett kraftfullt verktyg när man arbetar med halvstrukturerad text. Du kan använda dem för att validera textinmatning, städa upp det, förvandla det, normalisera det och i allmänhet hantera mycket mångfald med hjälp av kortfattat syntax. 

Go ger ett bibliotek med ett lättanvänt gränssnitt som består av ett Regexp-objekt med många metoder. Ge det ett försök, men akta dig för fallgroparna.