Compila una vez y luego busca
Escribe los patrones como literales raw string entre comillas invertidas. En un string normal entre comillas dobles cada barra invertida tiene que duplicarse ("\\d+"), lo que enseguida se vuelve ilegible.
regexp.MustCompile provoca un panic si el patrón no es válido, que es justo lo que quieres para un patrón escrito en tu código fuente: una errata falla al arrancar, no en la primera petición. Para los patrones que vienen de usuarios o de la configuración, usa regexp.Compile y maneja el error:
re, err := regexp.Compile(userPattern)
if err != nil {
return fmt.Errorf("bad pattern: %w", err)
}
Compila una vez. Compilar es mucho más caro que buscar. Un regexp.MustCompile dentro de una función que se ejecuta por cada petición o por cada línea repite ese trabajo cada vez. Guarda el *regexp.Regexp compilado en una var a nivel de paquete o en un campo de un struct. Un regexp compilado es seguro para usarlo desde muchas goroutines a la vez.
regexp.MatchString(pattern, s) compila y busca en una sola llamada. Está bien para una comprobación puntual y es un desperdicio dentro de un bucle.
Encontrar coincidencias
Los nombres de los métodos siguen un patrón: Find + All? + String? + Submatch? + Index?.
| Parte | Significado |
|---|---|
All | todas las coincidencias que no se solapan; recibe un límite n (-1 para todas) |
String | trabaja con un string; sin él, el método recibe y devuelve []byte |
Submatch | devuelve también los grupos de captura |
Index | devuelve desplazamientos en bytes en lugar del texto |
FindString devuelve "" cuando no hay coincidencia, lo que no se distingue de coincidir con un string vacío. Cuando el patrón puede coincidir con texto vacío, usa FindStringIndex (devuelve nil si no hay coincidencia) o MatchString antes. Las variantes All devuelven nil cuando nada coincide.
Grupos de captura
Los paréntesis capturan. FindStringSubmatch devuelve la coincidencia completa en el índice 0 y luego una entrada por grupo:
Comprueba siempre nil antes de indexar el resultado; si no hay coincidencia, m[1] provoca un panic.
(?:...) agrupa sin capturar, para alternancias o repeticiones: (?:ab)+. Los grupos con nombre se escriben (?P<name>...), y desde Go 1.22 también (?<name>...).
Reemplazar
Una trampa clásica con las referencias a grupos: "$1x" se lee como un grupo llamado 1x, que no existe, y se expande a un string vacío. Escribe "${1}x".
Para un string fijo, strings.ReplaceAll, strings.Contains y strings.Split son más sencillos y más rápidos que un regexp. Recurre a regexp cuando el texto que buscas tiene una forma en lugar de un valor fijo.
Referencia rápida de sintaxis
Go usa la sintaxis RE2, que cubre las funciones conocidas del estilo de Perl:
| Sintaxis | Coincide con |
|---|---|
. | cualquier carácter salvo el salto de línea (con (?s), también el salto de línea) |
\d \w \s | dígito, carácter de palabra [0-9A-Za-z_], espacio en blanco (solo ASCII) |
\D \W \S | las negaciones |
[abc] [^abc] [a-z] | clases de caracteres |
\pL \p{Greek} | clases Unicode: cualquier letra, cualquier carácter griego |
* + ? {n,m} | repetición, voraz |
*? +? ?? | repetición, perezosa |
^ $ | inicio y fin del texto (de la línea, con (?m)) |
\b | límite de palabra (ASCII) |
a|b | alternancia |
(?i) | sin distinguir mayúsculas desde ese punto hasta el final del grupo que lo contiene; normalmente se escribe al principio del patrón |
\d y \w solo coinciden con ASCII. Para "cualquier letra de cualquier idioma", usa \pL, y para cualquier dígito Unicode, \p{Nd}. regexp.QuoteMeta(s) escapa todos los caracteres especiales de s, lo que necesitas al construir un patrón a partir de la entrada del usuario.
Lo que RE2 no puede hacer
El regexp de Go garantiza que la búsqueda tarda un tiempo lineal respecto a la longitud de la entrada. Las funciones que romperían esa garantía no están soportadas:
- Sin lookahead ni lookbehind:
(?=...),(?!...),(?<=...)y(?<!...)son errores de compilación. Como Go 1.22 empezó a aceptar(?<name>...)para los grupos con nombre, un lookbehind falla ahora con el confuso mensajeinvalid named capture, como muestra el siguiente programa. - Sin referencias hacia atrás:
(\w)\1para encontrar una letra repetida no es posible. - Sin cuantificadores posesivos ni grupos atómicos.
El apaño es casi siempre el mismo: captura un poco más de lo que necesitas y luego filtra o recorta en Go. La recompensa es que un regexp de Go no puede caer en un backtracking catastrófico. Un patrón como (a+)+$, que puede congelar un motor PCRE con una entrada corta, aquí se ejecuta en tiempo lineal, lo que importa cuando el patrón o la entrada vienen de fuera.
Errores comunes
- Compilar dentro de un bucle o un handler. Compila una vez en una variable a nivel de paquete.
- Patrones entre comillas dobles.
"\d"ni siquiera es un string válido de Go; usa comillas invertidas. - Olvidar las anclas al validar.
\d{5}coincide dentro de"abc123456xyz". Usa^\d{5}$. - Indexar el resultado de un submatch sin comprobar
nil. - Esperar que
\wo\bentiendan texto que no es ASCII. Usa clases Unicode como\pL. - Usar regexp para strings fijos. El paquete
stringses más claro y más rápido.
Preguntas frecuentes
¿Cómo compruebo si un string coincide con una regex en Go?
Compila el patrón una vez con regexp.MustCompile, escribiéndolo como raw string entre comillas invertidas para que las barras invertidas no necesiten escape, y luego llama a re.MatchString(s). Sin anclas (^ y $), MatchString devuelve true si el patrón coincide en cualquier parte del string.
¿El regexp de Go admite lookahead y lookbehind?
No. El regexp de Go usa la sintaxis RE2, que no tiene lookahead, lookbehind ni referencias hacia atrás. A cambio, la búsqueda tarda un tiempo lineal respecto al tamaño de la entrada, así que un patrón o una entrada hostiles no pueden colgar tu programa. Usa grupos de captura y un poco de código Go, o divide la comprobación en dos regex.
¿Qué diferencia hay entre regexp.Compile y regexp.MustCompile?
Compile devuelve el patrón compilado y un error. MustCompile provoca un panic ante un patrón no válido. Usa MustCompile para los patrones escritos en tu código fuente, normalmente en una variable a nivel de paquete, para que una errata falle al arrancar. Usa Compile para los patrones que vienen de usuarios o de archivos de configuración.
¿Cómo obtengo los grupos de captura de una regex en Go?
Usa FindStringSubmatch, que devuelve un slice donde el índice 0 es la coincidencia completa y los índices 1, 2, ... son los grupos, o nil si no hay coincidencia. Para todas las coincidencias usa FindAllStringSubmatch(s, -1). Los grupos con nombre (?P<year>\d{4}) se pueden buscar con re.SubexpIndex("year").