El paquete regexp viene con Go y usa la sintaxis RE2, que tiene una garantía poco común: siempre se ejecuta en tiempo lineal, así que no existen los patrones que se cuelgan. A cambio no admite algunas cosas, como las retrorreferencias.
También te puede interesar
Compilar el patrón
re := regexp.MustCompile(`[\w.]+@[\w.]+\.\w+`)
Dos detalles de esa línea. Las comillas invertidas evitan tener que duplicar cada barra: con comillas normales habría que escribir "\\w". Y MustCompile revienta el programa si el patrón está mal, lo cual es exactamente lo que quieres cuando el patrón es una constante: el fallo aparece al arrancar y no a mitad de la ejecución.
var reCorreo = regexp.MustCompile(…).¿Coincide?
fmt.Println(regexp.MustCompile(`\d+`).MatchString("hay 42 unidades"))
salidatrue
Extraer
texto := "Contactos: ana@ejemplo.com (Madrid), luis.perez@correo.es (Lima), soporte@decodigo.com"
fmt.Println(re.FindString(texto)) // solo la primera
for _, m := range re.FindAllString(texto, -1) { // todas
fmt.Println(m)
}
salidaana@ejemplo.com ana@ejemplo.com luis.perez@correo.es soporte@decodigo.com
Ese -1 de FindAllString significa «todas las que haya». Si pones un número, se para ahí. Y si no encuentra nada, FindString devuelve la cadena vacía y FindAllString devuelve nil, que se puede recorrer sin problema.
Grupos con nombre
rg := regexp.MustCompile(`(?P<usuario>[\w.]+)@(?P<dominio>[\w.]+)`)
m := rg.FindStringSubmatch("ana@ejemplo.com")
for i, nombre := range rg.SubexpNames() {
if i > 0 && nombre != "" {
fmt.Printf("%-8s %s\n", nombre+":", m[i])
}
}
salidausuario: ana dominio: ejemplo.com
FindStringSubmatch devuelve un slice donde la posición 0 es la coincidencia entera y las siguientes son los grupos. SubexpNames() da los nombres en el mismo orden, y por eso se recorren a la vez. La posición 0 se salta porque no tiene nombre.
Sustituir
fmt.Println(re.ReplaceAllString(texto, "[correo oculto]"))
salidaContactos: [correo oculto] (Madrid), [correo oculto] (Lima), [correo oculto]
Y usando los grupos en el reemplazo, con $nombre:
fmt.Println(rg.ReplaceAllString("ana@ejemplo.com", "$usuario (en $dominio)"))
salidaana (en ejemplo.com)
Partir una cadena
sep := regexp.MustCompile(`\s*[,;]\s*`)
fmt.Printf("%q\n", sep.Split("uno, dos ;tres,cuatro", -1))
salida["uno" "dos" "tres" "cuatro"]
Útil cuando el separador varía o lleva espacios alrededor. Para un separador fijo y simple es mejor strings.Split, que no necesita compilar nada.
Cuando el patrón está mal
if _, err := regexp.Compile(`[a-`); err != nil {
fmt.Println(err)
}
salidaerror parsing regexp: missing closing ]: `[a-`
Esa es la diferencia entre las dos funciones: Compile devuelve un error y MustCompile provoca un panic. Usa MustCompile con patrones escritos por ti, y Compile cuando el patrón venga de fuera, de una configuración o del usuario.
Lo que RE2 no hace
| Esto no existe en Go | Por qué |
|---|---|
Retrorreferencias, (\w)\1 |
Obligarían a retroceder y romperían la garantía de tiempo lineal |
Lookahead y lookbehind, (?=…) |
Lo mismo |
| Patrones que se cuelgan | Justamente: no los hay. RE2 nunca entra en una explosión exponencial |
En la práctica se nota poco, y lo que se gana es que ningún patrón, venga de donde venga, puede tumbar el servicio.
Todo el código se compiló y ejecutó con Go 1.25 antes de publicarla; las salidas están copiadas de esa ejecución, incluido el error del patrón mal escrito. Documentación oficial: paquete regexp y su sintaxis.