Guía fundamental de expresiones regulares en Java

Las expresiones regulares constituyen un mecanismo robusto para identificar y manipular cadenas de texto. Operan mediante patrones definidos por caracteres y metacaracteres especiales.

El método group() de la clase Matcher

La clase Matcher internamente utiliza un arreglo de enteros (groups) para registrar las posiciones de coincidencia. Cada invocación exitosa de matcher.find() actualiza este arreglo con los índices de inicio y fin (el índice de fin está incrementado en uno) de la coincidencia completa y de cada grupo capturado por paréntesis.

En este arreglo:

  • Los índices 0 y 1 almacenan el inicio y fin de la coincidencia total.
  • Los índices 2 y 3 almacenan el inicio y fin de la captura del primer grupo entre paréntesis.
  • Los índices 4 y 5 corresponden al segundo grupo, y así sucesivamente.

La implementación de matcher.group() internamente llama a matcher.group(0), por lo que ambos métodos son funcionalmente idénticos.

Metacaracteres fundamentales

Secuencias de escape

Para representar literalmente un carácter que tiene significado especial en una expresión regular (como * . ? + ^ $ | \ / [ ] ( ) { }), se precede con una barra invertida (\). En el lenguaje Java, dado que la barra invertida es un carácter de escape en las cadenas, se deben escribir dos barras (\\) para representar una sola en la expresión regular.

Ejemplo de código (cambio de estructura y variables):

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

public class EjemploEscape {
    public static void main(String[] args) {
        String textoEntrada = "Buscar literal: [a] y \\n";
        String patronRegex = "\\[.\\]|\\\\n"; // Busca [cualquier_caracter] o la secuencia literal \n
        Pattern patronCompilado = Pattern.compile(patronRegex);
        Matcher buscador = patronCompilado.matcher(textoEntrada);
        List<String> hallazgos = new ArrayList<>();

        while (buscador.find()) {
            hallazgos.add(buscador.group());
        }
        System.out.println("Coincidencias: " + hallazgos);
    }
}

Salida: Coincidencias: [[a], \n]

Delimitadores de posición

^ y $ son anclas que coinciden con el inicio y el final de la cadena de entrada, respectivamente.

Ejemplo (contexto cambiado):

import java.util.regex.Pattern;

public class EjemploAnclas {
    public static void main(String[] args) {
        String linea = "2023-10-05 ERROR: Falla en el servicio.";
        boolean comienzaConFecha = Pattern.matches("^\\d{4}-\\d{2}-\\d{2}.*", linea);
        boolean terminaConPunto = Pattern.matches(".*\\.$", linea);
        System.out.println("Comienza con fecha: " + comienzaConFecha);
        System.out.println("Termina con punto: " + terminaConPunto);
    }
}

Salida: Comienza con fecha: true, Termina con punto: true

Cuantificadores

Los cuantificadores especifican cuántas veces debe ocurrir el elemento anterior.

  • * : Cero o más veces.
  • + : Una o más veces.
  • ? : Cero o una vez.
  • {n} : Exactamente n veces.
  • {n,} : Al menos n veces.
  • {n,m} : Entre n y m veces inclusive.

Ejemplo de código (lógica y nombres de variables modificados):

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

public class EjemploCuantificadores {
    public static void main(String[] args) {
        String secuencia = "a ab abb abbb";
        // Patrón: 'a' seguido de 1 a 3 'b's
        String definicionPatron = "ab{1,3}";
        Pattern patron = Pattern.compile(definicionPatron);
        Matcher detector = patron.matcher(secuencia);

        System.out.println("Coincidencias para 'ab{1,3}':");
        while (detector.find()) {
            System.out.println(detector.group());
        }
    }
}

Salida: ab, abb, abbb

El modo por defecto es codicioso (intenta coincidir con lo máximo posible). Añadiendo ? después de un cuantificador, se cambia al modo perezoso (intenta coincidir con lo mínimo necesario).

Ejemplo:

String texto = "<div><p>Contenido</p></div>";
String patronCodicioso = "<.*>"; // Coincide con <div><p>Contenido</p></div>
String patronPerezoso = "<.*?>"; // Coincide con <div>, <p>, </p>, </div>

Clases de caracteres y alternancia

  • . : Coincide con cualquier carácter excepto los terminadores de línea.
  • [abc] : Coincide con un conjunto de caracteres (a, b o c).
  • [^abc] : Coincide con cualquier carácter excepto los del conjunto.
  • [a-zA-Z0-9] : Coincide con un rango de caracteres.
  • (x|y) : Coincide con x o y (alternancia).

Ejemplo de código (reestructurado):

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

public class EjemploClases {
    public static void main(String[] args) {
        String datos = "ID: user_01, IP: 192.168.1.1, Error: N/A";
        // Patrón para extraer un identificador que empieza con 'user_' seguido de dígitos
        String patronId = "\\buser_\\d+\\b";
        Pattern compilado = Pattern.compile(patronId);
        Matcher extractor = compilado.matcher(datos);

        if (extractor.find()) {
            System.out.println("ID encontrado: " + extractor.group());
        }
    }
}

Salida: ID encontrado: user_01

Caracteres especiales de escape (clases abreviadas)

  • \d : Dígito, equivale a [0-9].
  • \D : No dígito, equivale a [^0-9].
  • \s : Carácter de espacio en blanco (espacio, tabulación, salto de línea, etc.).
  • \S : No espacio en blanco.
  • \w : Carácter de palabra (letra, dígito o guion bajo), equivale a [A-Za-z0-9_].
  • \W : No carácter de palabra.
  • \b : Límite de palabra (no consume caracteres).

Ejemplo:

String linea = "Contacto: admin_01@mail.com - Tel: 555-1234";
String patronEmail = "\\w+@\\w+\\.\\w+"; // Patrón simplificado para email

Grupos y referencias

Grupos capturadores (pattern): Encierran una sub-expresión y capturan el texto coincidente para su uso posterior mediante matcher.group(n).

Grupos no capturadores (?:pattern): Definen una sub-expresión para aplicar cuantificadores o alternancia, pero no capturan el texto, lo que puede mejorar el rendimiento.

Referencias internas \n: Hacen referencia al texto capturado por el n-ésimo grupo dentro de la misma expresión regular.

Referencias etxernas $n: Se usan en métodos como replaceAll() para insertar el texto capturado por el n-ésimo grupo.

Ejemplo de código (lógica modificada para validación de formato):

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

public class EjemploGrupos {
    public static void main(String[] args) {
        String fechaEnTexto = "Fecha válida: 25-12-2023. Otra: 5/1/24.";
        // Patrón para fecha con día y mes de 1 o 2 dígitos, año de 2 o 4 dígitos
        String patronFecha = "(\\d{1,2})[-/](\\d{1,2})[-/](\\d{2,4})";
        Pattern fechaPattern = Pattern.compile(patronFecha);
        Matcher fechaMatcher = fechaPattern.matcher(fechaEnTexto);

        while (fechaMatcher.find()) {
            System.out.println("Fecha completa: " + fechaMatcher.group());
            System.out.println("  Día: " + fechaMatcher.group(1));
            System.out.println("  Mes: " + fechaMatcher.group(2));
            System.out.println("  Año: " + fechaMatcher.group(3));
        }
    }
}

Salida:

Fecha completa: 25-12-2023
  Día: 25
  Mes: 12
  Año: 2023
Fecha completa: 5/1/24
  Día: 5
  Mes: 1
  Año: 24

Uso práctico con la API de Java

Clases principales

  • Pattern: Compila una expresión regular en un patrón reutilizable.
  • Matcher: Realiza operaciones de coincidencia sobre una cadena de entrada usando un patrón.

Compilar y usar un patrón

Se obtiene un Matcher mediante matcher.matcher(input). Luego se usan métodos como:

  • find(): Busca la siguiente subcadena que coincida con el patrón.
  • matches(): Comprueba si la totlaidad de la cadena coincide con el patrón.
  • lookingAt(): Comprueba si el inicio de la cadena coincide con el patrón.
  • replaceAll(replacement): Reemplaza todas las subcadenas que coinciden.
  • replaceFirst(replacement): Reemplaza la primera subcadena que coincide.

Ejemplo de reemplazo con referencia:

String textoConRepetidos = "Java es muy muy poderoso";
String patronRepetido = "(\\b\\w+\\b)\\s+\\1\\b"; // Captura una palabra y busca su repetición
Pattern p = Pattern.compile(patronRepetido);
Matcher m = p.matcher(textoConRepetidos);
String textoLimpio = m.replaceAll("$1"); // Mantiene solo la primera ocurrencia
System.out.println(textoLimpio); // Salida: "Java es muy poderoso"

Uso directo en la clase String

Los métodos matches(), replaceAll(), replaceFirst() y split() de la clase String aceptan una expresión regular como parámetro.

String correo = "usuario@ejemplo.com";
boolean esValido = correo.matches("[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}");
String sinEspacios = "hola   mundo".replaceAll("\\s+", " "); // "hola mundo"
String[] partes = "manzana,pera;uva".split("[,;]"); // ["manzana", "pera", "uva"]

Patrones comunes de validación

  • Solo caracteres chinos: [\u4E00-\u9FA5]+
  • Correo electrónico (simplificado): ^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$
  • Número de teléfono (formato genérico): ^\\+?\\d{7,15}$
  • Nombre de usuario (letras, números, _ y -, 4-16 chars): ^[a-zA-Z0-9_-]{4,16}$

Etiquetas: java expresiones regulares Pattern Matcher programación

Publicado el 6-17 19:15