CodeGym /Blog Java /Random-ES /Expresiones regulares en Java
Autor
Volodymyr Portianko
Java Engineer at Playtika

Expresiones regulares en Java

Publicado en el grupo Random-ES
Las expresiones regulares es un tema que los programadores, incluso los experimentados, suelen posponer para más adelante. Pero tarde o temprano, la mayoría de los desarrolladores de Java tienen que procesar información textual. La mayoría de las veces, esto significa buscar y editar texto. Sin expresiones regulares, el código de procesamiento de texto efectivo y compacto es simplemente impensable. Así que deja de procrastinar, abordemos las expresiones regulares ahora mismo. No es tan difícil. Expresiones regulares en Java - 1

¿Qué es una expresión regular (regex)?

De hecho, una expresión regular es un patrón para encontrar una cadena en el texto. En Java, la representación original de este patrón es siempre una cadena, es decir, un objeto de la Stringclase. Sin embargo, no es cualquier cadena que se pueda compilar en una expresión regular, solo cadenas que se ajustan a las reglas para crear expresiones regulares. La sintaxis se define en la especificación del lenguaje. Las expresiones regulares se escriben con letras y números, así como con metacaracteres, que son caracteres que tienen un significado especial en la sintaxis de las expresiones regulares. Por ejemplo:

String regex = "java"; // The pattern is "java";
String regex = "\\d{3}"; // The pattern is three digits;

Crear expresiones regulares en Java

La creación de una expresión regular en Java implica dos pasos simples:
  1. escríbalo como una cadena que cumpla con la sintaxis de expresiones regulares;
  2. compilar la cadena en una expresión regular;
En cualquier programa Java, empezamos a trabajar con expresiones regulares creando un Patternobjeto. Para hacer esto, necesitamos llamar a uno de los dos métodos estáticos de la clase: compile. El primer método toma un argumento: una cadena literal que contiene la expresión regular, mientras que el segundo toma un argumento adicional que determina la configuración de coincidencia de patrones:

public static Pattern compile (String literal)
public static Pattern compile (String literal, int flags)
La lista de valores potenciales del flagsparámetro se define en Patternclase y está disponible para nosotros como variables de clase estáticas. Por ejemplo:

Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE); // Pattern-matching will be case insensitive.
Básicamente, la Patternclase es un constructor de expresiones regulares. Bajo el capó, el compilemétodo llama al Patternconstructor privado de la clase para crear una representación compilada. Este mecanismo de creación de objetos se implementa de esta manera para crear objetos inmutables. Cuando se crea una expresión regular, se comprueba su sintaxis. Si la cadena contiene errores, se PatternSyntaxExceptiongenera un.

Sintaxis de expresiones regulares

La sintaxis de las expresiones regulares se basa en los <([{\^-=$!|]})?*+.>caracteres, que se pueden combinar con letras. Dependiendo de su función, se pueden dividir en varios grupos:
1. Metacaracteres para hacer coincidir los límites de líneas o texto
Metacarácter Descripción
^ principio de una linea
ps final de una línea
\b límite de palabras
\B límite de no palabra
\A comienzo de la entrada
\GRAMO final del partido anterior
\Z final de la entrada
\z final de la entrada
2. Metacaracteres para emparejar clases de caracteres predefinidas
Metacarácter Descripción
\d dígito
\D sin dígitos
\s carácter de espacio en blanco
\S carácter sin espacio en blanco
\w carácter alfanumérico o guión bajo
\W cualquier carácter excepto letras, números y guiones bajos
. cualquier personaje
3. Metacaracteres para emparejar caracteres de control
Metacarácter Descripción
\t carácter de tabulación
\norte carácter de nueva línea
\r retorno de carro
\F carácter de avance de línea
\u0085 carácter de la siguiente línea
\u2028 separador de linea
\u2029 separador de párrafo
4. Metacaracteres para clases de personajes
Metacarácter Descripción
[a B C] cualquiera de los caracteres enumerados (a, b o c)
[^ abc] cualquier carácter distinto de los enumerados (no a, b, o c)
[a-zA-Z] rangos combinados (caracteres latinos de la a a la z, sin distinción entre mayúsculas y minúsculas)
[anuncio[mp]] unión de caracteres (de la a a la d y de la m a la p)
[az&&[def]] intersección de caracteres (d, e, f)
[az&&[^bc]] resta de caracteres (a, dz)
5. Metacaracteres para indicar el número de caracteres (cuantificadores). Un cuantificador siempre va precedido de un carácter o grupo de caracteres.
Metacarácter Descripción
? uno o ninguno
* cero o más veces
+ una o más veces
{norte} n veces
{norte,} n o más veces
{Nuevo Méjico} al menos n veces y no más de m veces

Cuantificadores codiciosos

Una cosa que debe saber sobre los cuantificadores es que vienen en tres variedades diferentes: codiciosos, posesivos y reacios. Un cuantificador se convierte en posesivo agregando un +carácter " " después del cuantificador. Lo haces reacio agregando " ?". Por ejemplo:

"A.+a" // greedy
"A.++a" // possessive
"A.+?a" // reluctant
Intentemos usar este patrón para comprender cómo funcionan los diferentes tipos de cuantificadores. Por defecto, los cuantificadores son codiciosos. Esto significa que buscan la coincidencia más larga de la cadena. Si ejecutamos el siguiente código:

public static void main(String[] args) {
    String text = "Fred Anna Alexander";
    Pattern pattern = Pattern.compile("A.+a");
    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        System.out.println(text.substring(matcher.start(), matcher.end()));
    }
}
obtenemos esta salida:

Anna Alexa
Para la expresión regular " A.+a", la coincidencia de patrones se realiza de la siguiente manera:
  1. El primer carácter del patrón especificado es la letra latina A. Matcherlo compara con cada carácter del texto, comenzando desde el índice cero. El carácter Festá en el índice cero en nuestro texto, por lo que Matcheritera a través de los caracteres hasta que coincida con el patrón. En nuestro ejemplo, este carácter se encuentra en el índice 5.

    Expresiones regulares en Java - 2
  2. Una vez que se encuentra una coincidencia con el primer carácter del patrón, Matcherbusca una coincidencia con su segundo carácter. En nuestro caso, es el .carácter " ", que representa cualquier carácter.

    Expresiones regulares en Java - 3

    El personaje nestá en la sexta posición. Ciertamente califica como una coincidencia para "cualquier personaje".

  3. Matcherprocede a comprobar el siguiente carácter del patrón. En nuestro patrón, se incluye en el cuantificador que se aplica al carácter anterior: " .+". Debido a que el número de repeticiones de "cualquier carácter" en nuestro patrón es una o más veces, Matchertoma repetidamente el siguiente carácter de la cadena y lo compara con el patrón siempre que coincida con "cualquier carácter". En nuestro ejemplo, hasta el final de la cadena (del índice 7 al índice 18).

    Expresiones regulares en Java - 4

    Básicamente, Matcherengulle la cadena hasta el final; esto es precisamente lo que significa "codicioso".

  4. Después de que Matcher llega al final del texto y finaliza la verificación de la A.+parte " " del patrón, comienza a verificar el resto del patrón: a. No hay más texto avanzando, por lo que la verificación continúa "retrocediendo", comenzando desde el último carácter:

    Expresiones regulares en Java - 5
  5. Matcher"recuerda" el número de repeticiones en la .+parte " " del patrón. En este punto, reduce el número de repeticiones en una y compara el patrón más grande con el texto hasta que encuentra una coincidencia:

    Expresiones regulares en Java - 6

Cuantificadores posesivos

Los cuantificadores posesivos se parecen mucho a los codiciosos. La diferencia es que cuando el texto se ha capturado hasta el final de la cadena, no hay coincidencia de patrones mientras se "retrocede". En otras palabras, las primeras tres etapas son las mismas que para los cuantificadores codiciosos. Después de capturar toda la cadena, el comparador agrega el resto del patrón a lo que está considerando y lo compara con la cadena capturada. En nuestro ejemplo, usando la expresión regular " A.++a", el método principal no encuentra ninguna coincidencia. Expresiones regulares en Java - 7

Cuantificadores reacios

  1. Para estos cuantificadores, al igual que con la variedad codiciosa, el código busca una coincidencia basada en el primer carácter del patrón:

    Expresiones regulares en Java - 8
  2. Luego busca una coincidencia con el siguiente carácter del patrón (cualquier carácter):

    Expresiones regulares en Java - 9
  3. A diferencia de la coincidencia de patrones codiciosos, la coincidencia más corta se busca en la coincidencia de patrones reacia. Esto significa que después de encontrar una coincidencia con el segundo carácter del patrón (un punto, que corresponde al carácter en la posición 6 del texto), Matchercomprueba si el texto coincide con el resto del patrón: el carácter " a"

    Expresiones regulares en Java - 10
  4. El texto no coincide con el patrón (es decir, contiene el carácter " n" en el índice 7), por lo que Matcheragrega más un "cualquier carácter", porque el cuantificador indica uno o más. Luego vuelve a comparar el patrón con el texto en las posiciones 5 a 8:

    Expresiones regulares en Java - 11
  5. En nuestro caso, se encuentra una coincidencia, pero aún no hemos llegado al final del texto. Por lo tanto, la coincidencia de patrones se reinicia desde la posición 9, es decir, se busca el primer carácter del patrón usando un algoritmo similar y esto se repite hasta el final del texto.

    Expresiones regulares en Java - 12
En consecuencia, el mainmétodo obtiene el siguiente resultado cuando usa el patrón " A.+?a": Anna Alexa Como puede ver en nuestro ejemplo, diferentes tipos de cuantificadores producen resultados diferentes para el mismo patrón. Así que ten esto en cuenta y elige la variedad adecuada según lo que estés buscando.

Caracteres de escape en expresiones regulares

Debido a que una expresión regular en Java, o mejor dicho, su representación original, es un literal de cadena, debemos tener en cuenta las reglas de Java con respecto a los literales de cadena. En particular, el carácter de barra invertida " \" en los literales de cadena en el código fuente de Java se interpreta como un carácter de control que le dice al compilador que el siguiente carácter es especial y debe interpretarse de una manera especial. Por ejemplo:

String s = "The root directory is \nWindows"; // Move "Windows" to a new line
String s = "The root directory is \u00A7Windows"; // Insert a paragraph symbol before "Windows"
Esto significa que los literales de cadena que describen expresiones regulares y utilizan \caracteres " " (es decir, para indicar metacaracteres) deben repetir las barras invertidas para asegurarse de que el compilador de bytecode de Java no malinterprete la cadena. Por ejemplo:

String regex = "\\s"; // Pattern for matching a whitespace character
String regex = "\"Windows\"";  // Pattern for matching "Windows"
Las barras invertidas dobles también se deben usar para escapar de los caracteres especiales que queremos usar como caracteres "normales". Por ejemplo:

String regex = "How\\?";  // Pattern for matching "How?"

Métodos de la clase Pattern

La Patternclase tiene otros métodos para trabajar con expresiones regulares:
  • String pattern()‒ devuelve la representación de cadena original de la expresión regular utilizada para crear el Patternobjeto:

    
    Pattern pattern = Pattern.compile("abc");
    System.out.println(pattern.pattern()); // "abc"
    
  • static boolean matches(String regex, CharSequence input)– le permite comparar la expresión regular pasada como regex con el texto pasado como input. Devoluciones:

    verdadero: si el texto coincide con el patrón;
    falso – si no es así;

    Por ejemplo:

    
    System.out.println(Pattern.matches("A.+a","Anna")); // true
    System.out.println(Pattern.matches("A.+a","Fred Anna Alexander")); // false
    
  • int flags()‒ devuelve el valor del flagsconjunto de parámetros del patrón cuando se creó el patrón o 0 si el parámetro no se configuró. Por ejemplo:

    
    Pattern pattern = Pattern.compile("abc");
    System.out.println(pattern.flags()); // 0
    Pattern pattern = Pattern.compile("abc",Pattern.CASE_INSENSITIVE);
    System.out.println(pattern.flags()); // 2
    
  • String[] split(CharSequence text, int limit)– divide el texto pasado en una Stringmatriz. El limitparámetro indica el número máximo de coincidencias buscadas en el texto:

    • si limit > 0- limit-1coincide;
    • if limit < 0- todas las coincidencias en el texto
    • si limit = 0– todas las coincidencias en el texto, las cadenas vacías al final de la matriz se descartan;

    Por ejemplo:

    
    public static void main(String[] args) {
        String text = "Fred Anna Alexa";
        Pattern pattern = Pattern.compile("\\s");
        String[] strings = pattern.split(text,2);
        for (String s : strings) {
            System.out.println(s);
        }
        System.out.println("---------");
        String[] strings1 = pattern.split(text);
        for (String s : strings1) {
            System.out.println(s);
        }
    }
    

    Salida de la consola:

    
    Fred
    Anna Alexa
    ---------
    Fred
    Anna
    Alexa
    

    A continuación, consideraremos otro de los métodos de la clase utilizados para crear un Matcherobjeto.

Métodos de la clase Matcher

Las instancias de la Matcherclase se crean para realizar coincidencias de patrones. Matcheres el "motor de búsqueda" de expresiones regulares. Para realizar una búsqueda, necesitamos darle dos cosas: un patrón y un índice de inicio. Para crear un Matcherobjeto, la Patternclase proporciona el siguiente método: рublic Matcher matcher(CharSequence input) El método toma una secuencia de caracteres, que se buscará. Esta es una instancia de una clase que implementa la CharSequenceinterfaz. Puede pasar no solo un String, sino también un StringBuffer, StringBuilder, Segmento CharBuffer. El patrón es un Patternobjeto en el que matcherse llama al método. Ejemplo de creación de un emparejador:

Pattern p = Pattern.compile("a*b"); // Create a compiled representation of the regular expression
Matcher m = p.matcher("aaaaab"); // Create a "search engine" to search the text "aaaaab" for the pattern "a*b"
Ahora podemos usar nuestro "motor de búsqueda" para buscar coincidencias, obtener la posición de una coincidencia en el texto y reemplazar el texto usando los métodos de la clase. El boolean find()método busca la siguiente coincidencia en el texto. Podemos usar este método y una declaración de bucle para analizar un texto completo como parte de un modelo de evento. En otras palabras, podemos realizar las operaciones necesarias cuando ocurre un evento, es decir, cuando encontramos una coincidencia en el texto. Por ejemplo, podemos usar los métodos int start()y de esta clase int end()para determinar la posición de una coincidencia en el texto. Y podemos usar los métodos String replaceFirst(String replacement)y String replaceAll(String replacement)para reemplazar coincidencias con el valor del parámetro de reemplazo. Por ejemplo:

public static void main(String[] args) {
    String text = "Fred Anna Alexa";
    Pattern pattern = Pattern.compile("A.+?a");

    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        int start=matcher.start();
        int end=matcher.end();
        System.out.println("Match found: " + text.substring(start, end) + " from index "+ start + " through " + (end-1));
    }
    System.out.println(matcher.replaceFirst("Ira"));
    System.out.println(matcher.replaceAll("Mary"));
    System.out.println(text);
}
Producción:

Match found: Anna from index 5 through 8
Match found: Alexa from index 10 through 14
Fred Ira Alexa
Fred Mary Mary
Fred Anna Alexa
El ejemplo deja en claro que los métodos replaceFirsty replaceAllcrean un nuevo Stringobjeto: una cadena en la que las coincidencias de patrones en el texto original se reemplazan por el texto pasado al método como argumento. Además, el replaceFirstmétodo reemplaza solo la primera coincidencia, pero el replaceAllmétodo reemplaza todas las coincidencias en el texto. El texto original permanece sin cambios. Las operaciones de expresión regular más frecuentes de las clases Patterny Matcherestán integradas directamente en la Stringclase. Estos son métodos como split, matches, replaceFirsty replaceAll. Pero bajo el capó, estos métodos usan las clases Patterny Matcher. Entonces, si desea reemplazar texto o comparar cadenas en un programa sin escribir ningún código adicional, use los métodos delStringclase. Si necesita funciones más avanzadas, recuerde las clases Patterny Matcher.

Conclusión

En un programa Java, una expresión regular se define mediante una cadena que obedece reglas específicas de coincidencia de patrones. Al ejecutar el código, la máquina Java compila esta cadena en un Patternobjeto y usa un Matcherobjeto para encontrar coincidencias en el texto. Como dije al principio, la gente suele dejar las expresiones regulares para más adelante, considerándolas un tema difícil. Pero si comprende la sintaxis básica, los metacaracteres y el escape de caracteres, y estudia ejemplos de expresiones regulares, descubrirá que son mucho más simples de lo que parecen a primera vista.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION