Saltar al contenido
AldeaCode Logo
Tester Regex / Java Desarrollador 100% local

Tester de regex en Java: Pattern, Matcher, grupos nombrados y la trampa find vs matches

El regex de Java vive en java.util.regex con dos clases principales: Pattern y Matcher. La API es más verbosa que Python o JavaScript, pero el motor es rápido y el set de flags es completo. La trampa que pilla a todo el mundo es matches() versus find().

Pattern compila, Matcher empareja

El flujo es siempre el mismo. Compila un Pattern, obtén un Matcher contra un string, hazle preguntas al matcher:

Pattern p = Pattern.compile("\\b(\\d{3})-(\\d{4})\\b");
Matcher m = p.matcher("llama al 555-1234 hoy");
if (m.find()) {
    System.out.println(m.group()); // 555-1234
}

Las dobles barras invertidas son escapado del literal de string Java; el regex real ve \b y \d. Java 15+ añade text blocks (triple comillas) pero no cambian el significado del regex, solo el coste de escapado.

Pattern.compile tiene un coste moderado. Si el patrón es constante, súbelo a un private static final Pattern para compilar una vez y reutilizar. El Matcher es barato de crear y no es thread-safe; crea uno nuevo por cada input.

find() versus matches() versus lookingAt()

Tres métodos del matcher, tres semánticas distintas, todos devolviendo boolean. Elegir mal te da la respuesta incorrecta sin ruido.

matches() exige que el patrón consuma el input entero. Pattern.compile("\\d+").matcher("abc 123").matches() devuelve false porque abc no se consume. Para true necesitarías .*\\d+.* o usar find().

find() busca la siguiente subsecuencia que empareje en cualquier parte del input. El mismo ejemplo con find() devuelve true y m.group() es "123". Es lo que la mayoría de lenguajes llaman "match" y lo que espera la mayoría de desarrolladores.

lookingAt() empareja desde el inicio del input pero no exige que se consuma todo. Rara vez es la respuesta correcta; lo cito para que sepas que existe.

La regla: usa find() para buscar y extraer; usa matches() para validación de string completo, equivalente a anclar con ^ y $.

Flags de Pattern

Los flags de compilación cambian la semántica global del matching. Pásalos como segundo argumento a Pattern.compile o inline como (?i) dentro del patrón. Los cuatro más comunes:

Pattern.CASE_INSENSITIVE hace insensibles a mayúsculas las letras ASCII. Combínalo con Pattern.UNICODE_CASE para extender la regla a letras no ASCII. Sin él, Á y á se comparan distintas aunque case-insensitive esté activo.

Pattern.MULTILINE hace que ^ y $ casen en límites de línea dentro del input, no solo en inicio y fin del input completo. Necesario al escanear texto multilínea con patrones anclados a línea.

Pattern.DOTALL hace que . empareje también caracteres de nueva línea. El comportamiento por defecto es que . no empareja saltos. Útil al parsear bloques que abarcan varias líneas.

Pattern.UNICODE_CHARACTER_CLASS hace que \d, \w, \s y las clases POSIX usen categorías Unicode en lugar de ASCII. Importante cuando tu input tiene dígitos en scripts no latinos.

Lookbehind tiene historial por versión

El motor regex de Java soporta lookbehind, pero las reglas se han movido entre versiones. Antes de Java 9, lookbehind exigía longitud fija: (?<=foo) vale, (?<=fo+) no. Java 9 lo subió a longitud variable acotada, con un upper bound explícito en el cuantificador. Java 13 amplió el bound. Java 17 quitó el límite para la mayoría de patrones prácticos.

Si estás en JDK reciente (17 LTS o posterior, que es lo que apuntan la mayoría de equipos en 2026), puedes usar lookbehind de longitud variable sin pensar. En Java 8 (todavía común en enterprise legacy) la restricción de longitud fija es real y patrones que van en JavaScript o Python lanzan PatternSyntaxException al compilar.

El lookbehind negativo (? sigue las mismas reglas por versión que el positivo. Los grupos nombrados (?...) funcionan en cualquier JDK soportado y son claramente la forma correcta de escribir cualquier patrón con más de dos capturas.

Ejemplo completo

java
import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class RegexExample {
    // Sube el patrón: compila una vez, reutiliza siempre
    private static final Pattern EMAIL = Pattern.compile(
        "(?<local>[a-zA-Z0-9._%+-]+)@(?<domain>[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})",
        Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CHARACTER_CLASS
    );

    public static void main(String[] args) {
        String input = "Manda mail a ada@example.com o grace@example.org.";
        Matcher m = EMAIL.matcher(input);

        while (m.find()) {
            System.out.println("full:   " + m.group());
            System.out.println("local:  " + m.group("local"));
            System.out.println("domain: " + m.group("domain"));
        }

        // Validación: match de string completo, anclado implícitamente
        boolean valido = EMAIL.matcher("ada@example.com").matches();
        System.out.println("valido: " + valido);
    }
}

¿Solo necesitas el resultado?

Cuando estás depurando un patrón Java que falla con cierto input y quieres iterar sin recompilar una clase, pégalo en el tester de regex de aldeacode.com. La herramienta corre el patrón contra tu texto de muestra, resalta matches y muestra los grupos capturados, para que confirmes la forma antes de meterlo en Pattern.compile.

Abrir Tester de Expresiones Regulares (JavaScript) →

Preguntas frecuentes

¿Por qué mi regex va en JavaScript pero peta en Java?

Casi siempre un lookbehind de longitud variable en Java 8, o una feature como recursión que Java no implementa. Mira la versión de tu JDK y reduce el lookbehind a longitud fija si tienes que soportar Java 8.

¿Grupos nombrados o grupos numerados?

Grupos nombrados para cualquier patrón con más de dos capturas. Coste cero y ganancia de legibilidad grande. Los numerados están bien con una o dos capturas, sobre todo cuando el regex es corto.

¿El motor regex de Java sufre backtracking catastrófico?

Sí. Java usa un motor NFA con backtracking. Patrones con cuantificadores anidados tipo (a+)+ con input adversarial pueden colgarse segundos. Si procesas input no confiable, prefiere cuantificadores posesivos (a++) o grupos atómicos (?>...) para acotar el trabajo.