Expresiones regulares con Go Parte 2

Visión general

Esta es la segunda parte de una serie de dos tutoriales sobre expresiones regulares en Go. En la primera parte, aprendimos qué son las expresiones regulares, cómo expresarlas en Go y los conceptos básicos del uso de la biblioteca Go regexp para hacer coincidir el texto con los patrones de expresiones regulares.. 

En la segunda parte, nos centraremos en el uso de la biblioteca regexp en toda su extensión, incluida la compilación de expresiones regulares, la búsqueda de una o más coincidencias en el texto, la sustitución de expresiones regulares, la agrupación de submuestras y el tratamiento de nuevas líneas..

Usando la biblioteca de Regexp

La biblioteca de expresiones regulares proporciona soporte completo para expresiones regulares, así como la capacidad de compilar sus patrones para una ejecución más eficiente cuando se usa el mismo patrón para coincidir con varios textos. También puede encontrar índices de coincidencias, reemplazar coincidencias y grupos de uso. Vamos a bucear en.

Compilando tu Regex

Hay dos métodos para compilar expresiones regulares: Compilar() y MustCompile (). Compilar() devolverá un error si el patrón proporcionado no es válido. MustCompile () entrará en pánico. Se recomienda la compilación si le interesa el rendimiento y planea utilizar la misma expresión regular varias veces. Cambiemos nuestra partido() Función auxiliar para tomar un regex compilado. Tenga en cuenta que no es necesario verificar los errores porque la expresión regular compilada debe ser válida.

func match (r * regexp.Regexp, cadena de texto) matched: = r.MatchString (text) si coincide fmt.Println ("√", r.String (), ":", text) else fmt. Println ("X", r.String (), ":", text) 

Aquí es cómo compilar y usar la misma expresión regular compilada varias veces:

func main () es: = '(\ bcats? \ b) | (\ bdogs? \ b) | (\ brats? \ b)' e: = regexp.MustCompile (es) match (e, "Está lloviendo perros y los gatos ") coinciden (e," El catálogo está listo. ¡Es hora de hotdog! ") Combina (e," Es un mundo donde se comen perros. ") Salida: √ (\ bcats? \ b) | (\ bdogs? \ b) | (\ brats? \ b): Está lloviendo perros y gatos X (\ bcats? \ b) | (\ bdogs? \ b) | (\ brats? \ b): El catálogo está listo. ¡Es tiempo de hotdog! √ (\ bcats? \ B) | (\ bdogs? \ B) | (\ brats? \ B): Es un mundo para perros.. 

Hallazgo

El objeto Regexp tiene una mucho de FindXXX () metodos Algunos de ellos devuelven la primera coincidencia, otros devuelven todas las coincidencias y otros devuelven un índice o índices. Curiosamente, los nombres de los 16 métodos de funciones coinciden con la siguiente expresión regular: ¿Buscar (Todos)? (Cadena)? (Submatch)? (Índice)?

Si 'Todos' está presente, todas las coincidencias se devuelven frente a la izquierda. Si 'String' está presente, el texto de destino y los valores de retorno son cadenas frente a matrices de bytes. Si 'Submatch' está presente, se devuelven submatches (grupos) en lugar de simples coincidencias. Si 'Index' está presente, se devuelven los índices dentro del texto de destino en comparación con las coincidencias reales.

Tomemos una de las funciones más complejas de la tarea y usemos el FindAllStringSubmatch () método. Se necesita una cadena y un número. norte. Si norte es -1, devolverá todos los índices coincidentes. Si n es un entero no negativo, devolverá las n coincidencias más a la izquierda. El resultado es una rebanada de cortes de cadena. 

El resultado de cada sub-coincidencia es el partido completo seguido por el grupo capturado. Por ejemplo, considere una lista de nombres donde algunos de ellos tengan títulos como "Sr.", "Sra." O "Dr.". Aquí hay una expresión regular que captura el título como una sub-coincidencia y luego el resto del nombre después de un espacio: \ 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)) Salida: [[Dr. Dolittle Dr.]] [[Mrs. Duda Sra.] [Sr. Anderson Sr.]] 

Como se puede ver en la salida, la coincidencia completa se captura primero y luego solo el título. Para cada línea, la búsqueda se reinicia..

Reemplazo

Encontrar coincidencias es genial, pero a menudo es posible que deba reemplazar la coincidencia con otra cosa. El objeto Regexp tiene varios ReplaceXXX () los métodos habituales para tratar con cadenas frente a matrices de bytes y reemplazos literales frente a expansiones. En el gran libro 1984 por George Orwell, las consignas del partido están inscritas en la pirámide blanca del ministerio de la verdad: 

  • La guerra es paz 
  • Libertad es esclavitud 
  • Ignorancia es fuerza 

Encontré un pequeño ensayo sobre El precio de la libertad que utiliza algunos de estos términos. Corrigamos un fragmento de él de acuerdo con la expresión doble partido usando las expresiones regulares Go. Tenga en cuenta que algunas de las palabras de destino para el reemplazo utilizan mayúsculas diferentes. La solución es agregar el distintivo que distingue entre mayúsculas y minúsculas. (¿yo?) al comienzo de la expresión regular. 

Dado que la traducción es diferente según el caso, necesitamos un enfoque más sofisticado y luego un reemplazo literal. Afortunadamente (o por diseño), el objeto Regexp tiene un método de reemplazo que acepta una función que utiliza para realizar el reemplazo real. Definamos nuestra función de reemplazo que devuelve la traducción con el caso correcto.

func reemplazador (s cadena) cadena d: = mapa [cadena] cadena "guerra": "paz", "GUERRA": "PAZ", "Guerra": "Paz", "libertad": "esclavitud", " LIBERTAD ":" ESCLAVITUD "," Libertad ":" Esclavitud "," ignorancia ":" fortaleza "," IGNORANCIA ":" FUERZA "," Ignorancia ":" Fuerza ", r, ok: = d [s] si ok return r else return s 

Ahora, podemos realizar el reemplazo real:

func main () text: = 'EL PRECIO DE LA LIBERTAD: los estadounidenses en guerra Los estadounidenses han ido a la guerra para ganar su independencia, ampliar sus fronteras nacionales, definir sus libertades y defender sus intereses en todo el mundo'. expr: = '(??) (guerra | libertad | ignorancia)' r: = regexp.MustCompile (expr) resultado: = r.ReplaceAllStringFunc (texto, reemplazo) fmt.Println (resultado) Salida: EL PRECIO DE SLAVERY: American at Peace Los estadounidenses han ido a la paz para ganar su independencia, expandir sus fronteras nacionales, definir a sus esclavos y defender sus intereses en todo el mundo.. 

La salida es algo incoherente, que es el sello distintivo de la buena propaganda..

Agrupamiento

Ya vimos cómo usar la agrupación con submatches anteriormente. Pero a veces es difícil manejar múltiples sub-parches. Los grupos nombrados pueden ayudar mucho aquí. A continuación, le indicamos cómo nombrar sus grupos de subcoincidencia y llenar un diccionario para un fácil acceso por nombre:

func main () e: = '(? P\ w +) (? P.+ )?(?PAG\ w +) 'r: = regexp.MustCompile (e) names: = r.SubexpNames () fullNames: = [] string ' John F. Kennedy ',' Michael Jordan ' para _, fullName: = range fullNames result : = r.FindAllStringSubmatch (fullName, -1) m: = mapear [cadena] cadena  para i, n: = resultado del rango [0] m [nombres [i]] = n fmt.Println ("primer nombre : ", m [" first "]) fmt.Println (" middle_name: ", m [" middle "]) fmt.Println (" last name: ", m [" last "]) fmt.Println () Salida: nombre de pila: nombre del medio de Juan: nombre de F. F. nombre de Kennedy: nombre del segundo nombre de Michael: apellido: Jordania

Tratar con nuevas líneas

Si recuerdas, dije que el carácter especial del punto coincide con cualquier carácter. Bueno, mentí. No coincide con la nueva línea (\norte) carácter por defecto. Eso significa que sus coincidencias no cruzarán líneas a menos que lo especifique explícitamente con la bandera especial (? s) que puede agregar al comienzo de su expresión regular. Aquí hay un ejemplo con y sin la bandera..

func main () text: = "1111 \ n2222" expr: = [] string ". *", "(? s). *" para _, e: = range expr r: = regexp.MustCompile ( e) resultado: = r.FindString (texto) resultado = cadenas.Reemplazo (resultado, "\ n", '\ n', -1) fmt.Println (e, ":", resultado) fmt.Println ()  Salida:. *: 1111 (? S). *: 1111 \ n2222 

Otra consideración es si tratar el ^ y PS caracteres especiales como el principio y el final de todo el texto (el valor predeterminado) o como el principio y el final de cada línea con el (?metro) bandera.  

Conclusión

Las expresiones regulares son una herramienta poderosa cuando se trabaja con texto semiestructurado. Puede usarlos para validar la entrada de texto, limpiarla, transformarla, normalizarla y, en general, tratar con una gran diversidad utilizando una sintaxis concisa.. 

Go proporciona una biblioteca con una interfaz fácil de usar que consta de un objeto Regexp con muchos métodos. Pruébalo, pero ten cuidado con las trampas..