Dit is deel twee van een tweedelige reeks zelfstudies over reguliere expressies in Go. In deel een hebben we geleerd wat reguliere expressies zijn, hoe deze in Go kunnen worden uitgedrukt en de basisprincipes van het gebruik van de Go-regexp-bibliotheek om tekst aan te passen aan reguliere expressiepatronen.
In deel twee zullen we ons concentreren op het volledig benutten van de regexp-bibliotheek, inclusief het samenstellen van reguliere expressies, het vinden van een of meer overeenkomsten in de tekst, het vervangen van reguliere expressies, het groeperen van submatches en het omgaan met nieuwe regels..
De regexp-bibliotheek biedt volwaardige ondersteuning voor reguliere expressies en de mogelijkheid om uw patronen te compileren voor een efficiëntere uitvoering wanneer hetzelfde patroon wordt gebruikt dat overeenkomt met meerdere teksten. U kunt ook indices van overeenkomsten vinden, overeenkomsten vervangen en groepen gebruiken. Laten we erin duiken.
Er zijn twee methoden voor het compileren van regexes: Compileren()
en MustCompile ()
. Compileren()
retourneert een fout als het opgegeven patroon ongeldig is. MustCompile ()
zal in paniek raken. Compilatie wordt aanbevolen als u om prestaties geeft en van plan bent dezelfde regex meerdere keren te gebruiken. Laten we onze veranderen wedstrijd()
helperfunctie om een gecompileerde regex te maken. Merk op dat het niet nodig is om te controleren op fouten, omdat de gecompileerde regex geldig moet zijn.
func match (r * regexp.Regexp, text string) matched: = r.MatchString (text) if matched fmt.Println ("√", r.String (), ":", text) else fmt. Println ("X", r.String (), ":", tekst)
Hier leest u hoe u dezelfde gecompileerde regex meerdere keren kunt compileren en gebruiken:
func main () es: = '(\ bcats? \ b) | (\ bdogs? \ b) | (\ brats? \ b)' e: = regexp.MustCompile (es) match (e, "Het regent honden en katten ") match (e," De catalogus is klaar. Het is hotdog tijd! ") match (e," Het is een hond eet hondenwereld. ") Uitgang: √ (\ bcats? \ b) | (\ bdogs? \ b) | (\ brats? \ b): Het regent honden en katten X (\ bcats? \ b) | (\ bdogs? \ b) | (\ brats? \ b): De catalogus is klaar. Het is hotdogtijd! √ (\ bcats? \ B) | (\ bdogs? \ B) | (\ brats? \ B): het is een hondeneten wereld.
Het Regexp-object heeft een lot van FindXXX ()
methoden. Sommigen van hen geven de eerste match terug, anderen retourneren alle matches en weer anderen retourneren een index of indexen. Interessant genoeg komen de namen van alle 16 methoden van functies overeen met de volgende regex: Vind (alle)? (String)? (Submatch)? (Index)?
Als 'Alle' aanwezig is, worden alle treffers geretourneerd versus de meest linkse. Als 'String' aanwezig is, zijn de doeltekst en de retourwaarden tekenreeksen versus bytearrays. Als 'Submatch' aanwezig is, worden submatches (groepen) geretourneerd versus eenvoudige overeenkomsten. Als 'Index' aanwezig is, worden indexen binnen de doeltekst geretourneerd versus de daadwerkelijke overeenkomsten.
Laten we een van de meer complexe functies naar de taak brengen en de gebruiken FindAllStringSubmatch ()
methode. Het heeft een string en een nummer nodig n
. Als n
is -1, het zal alle overeenkomende indices retourneren. Als n een niet-negatief geheel getal is, retourneert dit de n meest linkse overeenkomsten. Het resultaat is een plakje snarenplakken.
Het resultaat van elke submatch is de volledige match gevolgd door de vastgelegde groep. Beschouw bijvoorbeeld een lijst met namen waar sommigen van hen titels als 'meneer', 'mevrouw' of 'dr.' Hebben. Hier is een regex die de titel vangt als een submatch en vervolgens de rest van de naam na een spatie: \ b (Mr \. | Mevrouw \. | 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)) Uitvoer: [[Dr. Dolittle Dr.]] [[Mevr. Doubtfire Mrs.] [Mr. Anderson Mr.]]
Zoals je kunt zien in de uitvoer, wordt de volledige overeenkomst eerst vastgelegd en dan alleen de titel. Voor elke regel wordt de zoekopdracht gereset.
Het vinden van wedstrijden is geweldig, maar vaak moet je de wedstrijd vervangen door iets anders. Het object Regexp heeft verschillende ReplaceXXX ()
methoden zoals gebruikelijk voor het omgaan met tekenreeksen versus byte-arrays en letterlijke vervangingen versus uitbreidingen. In het grote boek 1984 door George Orwell zijn de leuzen van de partij gegraveerd op de witte piramide van de bediening van de waarheid:
Ik vond een klein essay over The Price of Freedom dat sommige van deze termen gebruikt. Laten we een fragment ervan corrigeren op basis van de party doublespeak met behulp van Go-regexes. Merk op dat sommige van de doelwoorden voor vervanging verschillende hoofdletters gebruiken. De oplossing is om de hoofdletterongevoelige vlag toe te voegen (ik?)
aan het begin van de regex.
Omdat de vertaling afhankelijk van het geval anders is, hebben we een meer verfijnde aanpak nodig dan letterlijke vervanging. Gelukkig (of door ontwerp), heeft het Regexp-object een vervangingsmethode die een functie accepteert die het gebruikt om de daadwerkelijke vervanging uit te voeren. Laten we onze vervangfunctie definiëren die de vertaling retourneert met de juiste casus.
func replacer (s string) string d: = map [string] string "war": "peace", "WAR": "PEACE", "War": "Peace", "freedom": "slavery", " FREEDOM ":" SLAVERY "," Freedom ":" Slavery "," ignorance ":" strength "," IGNORANCE ":" STRENGTH "," Ignorance ":" Strength ", r, ok: = d [s] if ok return r else return s
Nu kunnen we de daadwerkelijke vervanging uitvoeren:
func main () text: = 'DE PRIJS VAN VRIJHEID: Amerikanen in oorlog Amerikanen zijn ten oorlog getrokken om hun onafhankelijkheid te winnen, hun nationale grenzen uit te breiden, hun vrijheden te definiëren en hun belangen overal ter wereld te verdedigen.' expr: = '(? i) (oorlog | vrijheid | onwetendheid)' r: = regexp.MustCompile (expr) result: = r.ReplaceAllStringFunc (text, replacer) fmt.Println (result) Output: THE PRICE OF SLAVERY: Amerikanen bij vrede Amerikanen zijn naar vrede gegaan om hun onafhankelijkheid te winnen, hun nationale grenzen uit te breiden, hun slavernij te definiëren en hun belangen overal ter wereld te verdedigen.
De output is enigszins incoherent, wat het kenmerk is van goede propaganda.
We hebben gezien hoe groepering met submatches eerder moest worden gebruikt. Maar het is soms moeilijk om meerdere submatches af te handelen. Benoemde groepen kunnen hier veel helpen. Hier leest u hoe u uw submatch-groepen een naam geeft en een woordenboek vult voor gemakkelijke toegang op naam:
func main () e: = '(? P\ w +) (? P .+ )? (? P \ w +) 'r: = regexp.MustCompile (e) namen: = r.SubexpNames () fullNames: = [] string ' John F. Kennedy ',' Michael Jordan ' voor _, fullName: = bereik fullNames resultaat : = r.FindAllStringSubmatch (fullName, -1) m: = kaart [tekenreeks] tekenreeks voor i, n: = bereikresultaat [0] m [namen [i]] = n fmt.Println ("voornaam : ", m [" eerste "]) fmt.Println (" middle_name: ", m [" middle "]) fmt.Println (" achternaam: ", m [" last "]) fmt.Println () Output: voornaam: John middle_name: F. achternaam: Kennedy voornaam: Michael middle_name: achternaam: Jordan
Als je het onthoudt, zei ik dat het speciale teken voor een punt overeenkomt met een willekeurig teken. Wel, ik heb gelogen. Het komt niet overeen met de nieuwe regel (\ n
) standaard in. Dat betekent dat uw overeenkomsten geen lijnen kruisen, tenzij u dit expliciet opgeeft met de speciale vlag (? S)
die u aan het begin van uw regex kunt toevoegen. Hier is een voorbeeld met en zonder de vlag.
func main () text: = "1111 \ n2222" expr: = [] string ". *", "(? s). *" voor _, e: = range expr r: = regexp.MustCompile ( e) resultaat: = r.FindString (text) result = strings.Replace (result, "\ n", '\ n', -1) fmt.Println (e, ":", result) fmt.Println () Uitvoer:. *: 1111 (? S). *: 1111 \ n2222
Een andere overweging is of de ^
en $
speciale tekens als het begin en het einde van de hele tekst (de standaard) of als het begin en het einde van elke regel met de (? M)
vlag.
Reguliere expressies zijn een krachtig hulpmiddel bij het werken met semi-gestructureerde tekst. Je kunt ze gebruiken om tekstuele invoer te valideren, op te ruimen, te transformeren, te normaliseren en in het algemeen veel diversiteit aan te pakken met behulp van beknopte syntaxis.
Go biedt een bibliotheek een eenvoudig te gebruiken interface die bestaat uit een Regexp-object met vele methoden. Probeer het eens, maar pas op voor de valkuilen.