Expresiones regulares en Java: Guía técnica con ejemplos

Las expresiones regulares en Java permiten el procesamiento y validación de cadenas de texto mediante patrones deifnidos. A continuación, se exploran conceptos clave y ejemplos prácticos.

Uso básico de expresiones regulares

En Java, el método matches() de la clase String verifica si una cadena coincide con un patrón. Por ejemplo:

public class EjemploRegex {
    public static void main(String[] args) {
        // Verificar coincidencia con un patrón de tres caracteres
        boolean coincide = "xyz".matches("...");
        System.out.println(coincide); // true

        // Reemplazar dígitos en una cadena
        String texto = "b98729b";
        String resultado = texto.replaceAll("\\d", "*");
        System.out.println(resultado); // b*****b
    }
}

En el patrón, cada punto (.) representa un carácter arbitrario. La barra invertida debe escaparse en Java con doble contrabarra: \\d coincide con cualquier dígito.

Clases Pattern y Matcher

Para un mejor rendimiento y funcionalidad avanzada, se utilizan las clases Pattern y Matcher:

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

Pattern patronCompilado = Pattern.compile("[A-Z]{3}");
Matcher matcher = patronCompilado.matcher("ABC");
System.out.println(matcher.matches()); // true

// Equivalente a:
System.out.println("ABC".matches("[A-Z]{3}")); // true

La compilación del patrón con Pattern.compile() optimiza la ejecución. Matcher proporciona métodos como find() para buscar subcadenas y group() para extraer coincidencias.

Metacaracteres: . * + ?

Estos caracteres especiales definen cuantificadores y comodines:

System.out.println("b".matches(".")); // true, . coincide con cualquier carácter
System.out.println("bbb".matches("b*")); // true, * cero o más repeticiones
System.out.println("bbb".matches("b+")); // true, + una o más repeticiones
System.out.println("b".matches("b?")); // true, ? cero o una repetición

// Ejemplo con rangos numéricos
System.out.println("67890".matches("\\d{4,6}")); // true, de 4 a 6 dígitos
System.out.println("10.0.0.1".matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")); // true

El punto literal se escapa como \\. en el patrón.

Conjuntos de caracteres y rangos

Los corchetes [] definen un conjunto de caracteres para coincidencia individual:

System.out.println("c".matches("[abc]")); // true, conjunto simple
System.out.println("x".matches("[^abc]")); // true, ^ niega el conjunto
System.out.println("Z".matches("[a-zA-Z]")); // true, rango de letras
System.out.println("Y".matches("[A-Z&&[WXY]]")); // true, intersección de conjuntos

Para incluir caracteres como guion (-) dentro del conjunto, se escapa: [-] o [\\-].

Límites y bordes

Los caracteres de borde coinciden con posiciones específicas en la cadena:

System.out.println("inicio".matches("^in.*")); // true, ^ inicio de línea
System.out.println("fin".matches(".*in$")); // true, $ final de línea
System.out.println("palabra".matches("\\bpalabra\\b")); // true, \b límite de palabra

Métodos matches, find y lookingAt

Estos métodos de Matcher tienen comportamientos distintos:

Pattern patron = Pattern.compile("\\d{2,4}");
String cadena = "12-345-6789-00";
Matcher matcher = patron.matcher(cadena);

System.out.println(matcher.matches()); // false, no toda la cadena coincide
matcher.reset(); // Reiniciar el estado del matcher

while (matcher.find()) {
    System.out.println("Coincidencia: " + matcher.group() + " en " + matcher.start() + "-" + matcher.end());
}
// Salida: Coincidencia: 345 en 3-6, Coincidencia: 6789 en 7-11

System.out.println(matcher.lookingAt()); // true, siempre busca desde el inicio de la cadena

matches() requiere coincidencia exacta de toda la cadena, mientras que find() busca subcadenas coincidentes.

Grupos de captura y reemplazo

Los grupos definidos por paréntesis capturan partes de la coincidencia:

Pattern patron = Pattern.compile("(\\d{1,2})-([a-zA-Z]+)");
String datos = "23-abc, 456-def";
Matcher matcher = patron.matcher(datos);

while (matcher.find()) {
    System.out.println("Número: " + matcher.group(1));
    System.out.println("Texto: " + matcher.group(2));
}

Para reemplazos condicionales, se pueden usar appendReplacement() y appendTail() con un StringBuffer.

Grupos no capturantes y lookahead

Los grupos no capturantes y los lookahead permiten patrones avanzados sin consumir caracteres:

// Lookahead positivo: coincidir tres caracteres seguidos de 'a'
Pattern patron = Pattern.compile(".{3}(?=a)");
String texto = "123a456";
Matcher matcher = patron.matcher(texto);
while (matcher.find()) {
    System.out.println(matcher.group()); // 123
}

// Lookbehind negativo: coincidir tres caracteres no precedidos por 'b'
Pattern patron2 = Pattern.compile("(?<!b).{3}");
String texto2 = "bcd456";
Matcher matcher2 = patron2.matcher(texto2);
while (matcher2.find()) {
    System.out.println(matcher2.group()); // cd4
}

Banderas de configuración

Se puedan aplicar banderas para modificar el comportamiento, como ignorar mayúsculas y minúsculas:

Pattern patron = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
System.out.println(patron.matcher("Java").matches()); // true

// Uso de bandera incrustada (?i)
System.out.println("JAVA".matches("(?i)java")); // true

Otras banderas incluyen Pattern.DOTALL para que el punto coincida con saltos de línea.

Etiquetas: java Regex pattern-matcher regular-expressions string-processing

Publicado el 7-12 00:09