EXPRESIONES REGULARES Java Regex - Sintaxis de expresiones regulares La búsqueda de caracteres o secuencias concretas de caracteres en documentos forma parte de las tareas estándar y recurrentes de la tecnología de la información. Por lo general, el objetivo es modificar o sustituir fragmentos de texto o líneas de código, cuya complejidad aumenta en función de las veces que la secuencia de caracteres aparece en el documento. En la década de 1950 se encontró una solución basada en las lenguas formales de la informática teórica que sigue presente en el desarrollo actual de software y que permite simplificar estas tareas repetitivas mediante el uso de las denominadas expresiones regulares (en inglés, regular expressions). Una expresión regular define un patrón de búsqueda de cadenas. La abreviatura de expresión regular es regex. El patrón de búsqueda puede ser cualquier cosa, desde un carácter simple, una cadena fija o una expresión compleja que contiene caracteres especiales que describen el patrón. El patrón definido por la expresión regular puede coincidir una o varias veces o no coincidir en absoluto para una cadena determinada. Las regex son las unidades de descripción de los lenguajes regulares, que se incluyen en los denominados lenguajes formales. Son un instrumento clave de la informática teórica, la cual, entre otras cosas, establece las bases para el desarrollo y la ejecución de programas informáticos, así como para la construcción del compilador necesario para ello. Es por esto que las expresiones regulares, también denominadas regex y basadas en reglas sintácticas claramente definidas, se utilizan principalmente en el ámbito del desarrollo de software. El proceso de analizar o modificar un texto con una expresión regular se llama: La expresión regular se aplica al texto / cadena. El patrón definido por la expresión regular se aplica al texto de izquierda a derecha. Una vez que se ha utilizado un carcter de origen en una busqueda, no se puede reutilizar. Por ejemplo, la expresión regular aba coincidirá con ababababa solo dos veces (aba_aba__). ¿Qué reglas sintácticas son válidas para las expresiones regulares? Las expresiones regulares se pueden aplicar en diversos lenguajes, tales como Perl, Python, Ruby, JavaScript, XML o HTML, por lo que los usos o funciones pueden llegar a ser muy diferentes. En JavaScript los patrones regex se utilizan, por ejemplo, en los métodos de cadena search(), match() o replace(), mientras que las expresiones en documentos XML sirven para limitar elementos de contenido. En lo que respecta a la sintaxis, entre los diferentes lenguajes de programación y lenguajes de marcado apenas hay diferencias en cuanto a las expresiones regulares. Así, una expresión regular puede estar formada por hasta tres partes, independientemente del lenguaje en el que se va a utilizar: Pattern (patrón El elemento central es el patrón, esto es, el patrón de búsqueda de búsqueda) general. Tal y como hemos explicado antes, se puede formar a partir de caracteres simples o a partir de una combinación de caracteres simples y especiales. Delimiter (delimitador) El inicio y el final del patrón se identifican con delimitadores. Los delimitadores son, básicamente, todos los caracteres no alfanuméricos (excepto la barra diagonal inversa). Por ejemplo, para PHP las almohadillas (#pattern#), los signos de porcentaje (%pattern%), el signo más (+pattern+) o las tildes (~pattern~) son delimitadores. La mayoría de lenguajes ya usan las comillas (“pattern”) o las barras diagonales (/pattern/). Modifier (modificador) Los modificadores pueden añadirse a un patrón de búsqueda para modificar la expresión regular. Un ejemplo es el modificador i, el cual anula la distinción entre mayúsculas y minúsculas. Garantiza que las mayúsculas y las minúsculas se tienen en consideración y que valen por defecto para todas las expresiones regulares. Para usar expresiones regulares de manera efectiva en Java, necesita conocer la sintaxis. La sintaxis es extensa, lo que le permite escribir expresiones regulares muy avanzadas. Puede que sea necesario mucho ejercicio para dominar completamente la sintaxis. En este texto, analizaré los conceptos básicos de la sintaxis con ejemplos. No cubriré cada pequeño detalle de la sintaxis, pero me enfocaré en los conceptos principales que necesita comprender para trabajar con expresiones regulares. Para obtener una explicación completa, consulte la página JavaDoc de la clase Pattern. Sintaxis básica Antes de mostrar todas las opciones avanzadas que puede usar en las expresiones regulares de Java, le daré un resumen rápido de los conceptos básicos de la sintaxis de las expresiones regulares de Java. También puede hacer referencia a símbolos a través de sus códigos octales, hexadecimales o Unicode. A continuación, se muestran dos ejemplos: \0101 \x41 \u0041 Éstas tres expresiones se refieren al símbolo A en mayúscula. El primero usa el código octal (101) para A, el segundo usa el código hexadecimal (41) y el tercero usa el código Unicode (0041). Expresiones regulares de un elemento La forma regex más sencilla es un patrón de búsqueda que tan solo prevé un único elemento como resultado. Este tipo de expresión regular de un elemento puede, por ejemplo, definirse sin problemas usando una clase de caracteres, siempre y cuando no se esté buscando un elemento concreto. La siguiente expresión permite opcionalmente los dígitos “1”, “2”, “3”, “4”, “5”, “6” o “7” como posible resultado: [1234567] [1245367] [7654321] … O cualquier combinación de ellos referencían a la misma clase de caracteres Es importante denotar que los corchetes cuadrados representan si y solo si un carácter. Este ejemplo busca todas las apariciones de la palabra John, con una J minúscula o mayúscula: [Jj]ohn La clase de símbolos [Jj] coincidirá con una J (Mayuscula) o con una j (minúscula), y el resto de la expresión coincidirá con los símbolos ohn en esa secuencia y longitud exacta. En el caso anterior, los números son directamente consecutivos (del 1 al 7), por lo que también se permite la siguiente grafía simplificada: [1-7] [1234567] Que hace referencia a la clase del símbolo del 1 al 7. En caso de que la expresión regular deba modificarse para excluir de la búsqueda el dígito “4”, también se puede utilizar la variante más simple con el signo menos: [1-35-7] [123567] Nota: Los caracteres de un patrón regex no se separan con espacios. Expresiones regulares de varios elementos En el caso de las expresiones regulares de varios elementos, también se puede trabajar con clases de caracteres para permitir resultados diferentes. Si la expresión tiene que incluir, por ejemplo, dos elementos para los que se pueden esperar diferentes resultados, entonces basta con colocar dos clases de caracteres una después de la otra: [1-7][a-c] [1234567][abc] … 1a, 1b, 1c, 2a, 2b, 2c, … Como primer elemento, ha de aparecer un número de entre el “1” y el “7”, seguido de una letra “a”, “b” o “c”. Recordamos que aquí las minúsculas son obligatorias. Sin profundizar en los modificadores, con este pequeño ajuste de la expresión puedes incluir las mayúsculas: [1-7][a-cA-C] … 1a, 1A, 1b, 1B, 1c, 1C, 2a, 2A, … ¿Qué representan los siguientes patrones?: 1. [A-Z][A-Z] [A-Z] [A-Z] [0-9] [0-9] [0-9] [0-9] [0-9] [0-9] 2. [+-][$ç][0-9] [0-9] [0-9][,] [0-9] [0-9] [0-9][,] [0-9] [0-9] [0-9][.] [0-9] [0-9] Comúnmente las llaves son caracteres literales cuando se utilizan por separado en una expresión regular. Para que adquieran su función de metacaracteres es necesario que encierren uno o varios números separados por coma y que estén colocados a la derecha de otra expresión regular para los ejemplos anteriores sería de la siguiente forma: [A-Z]{4}[0-9]{6} [+-][$Ç][0-9]{3}[,][0-9]{3}[0-9][3][.][0-9]{2} Son utilizados para indicar el número de veces que puede darse una coincidencia. Un número entre llaves ({3}) indica el número exacto de coincidencias. Dos números separados por una coma ({2,4}), indica que puede coincidir al menos tantas veces como el primer número (2), y cómo máximo tantas veces como el segundo número (4). De manera similar, {2,} significa que al menos ocurre dos veces y {,4}, que como máximo ocurre cuatro veces. Las varaciones en las llaves nos permiten ampliar las posibilidades de reconocimiento en los patrones, por ejemplo: [0-9]{2,6} Nos refiere a que símbolo [0-9] se puede repetir como mínimo 2 veces y como máximo 6 veces, tal como se puede observar a continuación: [0-9]{2,6} … 12 se repite 2 veces 123 se repite 3 veces 1234 se repite 4 veces 12345 se repite 5 veces 123456 se repite 6 veces 1 1234567 no es válido, como mínimo es una repetición de 2 veces no es válido, como máximo es una repetición de 6 veces Existen casos en que no se tiene determinado el máximo o el mínimo de repeticiones para un símbolo como en los espacios en blanco entre palabras o el número de dígitos fraccionarios en un numero decimal. [+-]\s{1,}[0-9]{2,6} El \s (espacio en blanco) se puede repetir 1 o muchas veces (sin determinar) … - 22 1 espacio en blanco - 22 2 espacios en blanco 22 3 espacios en blanco … [0-9]{,4} {2, } El número puede estar conformado como máximo de 4 cifras … 1 1 cifra 12 2 cifras 123 3 cifras 1234 4 cifras Nota: Cuando no se establece el límite inferior se considera que se puede repetir 0 veces, es decir una cadena vacía. Existen algunos atajos que nos permiten escribir expresiones regulares más compactas: • • • \w \d \s caracteres de palabras dígitos espacio en blanco ([a-zA-Z0-9_]) ([0-9]) ([\t\r\n ]) También existen atajos de negación para los anteriores: • • • \W \D \S [^\w] – Cualquier símbolo menos palabra [^\d] - Cualquier símbolo menos dígito [^\s] - Cualquier símbolo menos espacio en blanco Nota: Si en un expresión regular queremos incluir el carácter \ como símbolo, tendremos que escribirlo dos veces: \\. Para los ejemplos anteriores se podrían reescribir de la siguiente forma: [A-Z]{4}\d{6} [+-][$Ç]\d{3}[,]\d{3}[,]\d[3][.]\d{2} En ciertas ocasiones se requiere que los números, las comas y el punto tengan un espacio para una mejor lectura, es por eso que podríamos cambiar nuestro ejemplo de la siguiente forma: [+-][$Ç]\d{3}\s[,]\d{3}[,]\s\d[3][.]\s\d{2} … -$123 , 456 , 789 . 89 Al margen de si se buscan varios elementos en una única expresión regular o con ayuda de varios grupos de caracteres, es posible que determinados elementos solo se deban o puedan incluir en determinadas condiciones. Este puede ser, por ejemplo, el caso de una expresión regular que debe filtrar todos los números de casa de las direcciones. En los casos en los que el número de casa sea un único dígito, puede coincidir con algunos resultados en los que el número está compuesto por dos o incluso tres dígitos. Además, hay direcciones en las que el número de casa incluye también una letra. Para abarcar todas estas posibles combinaciones, puedes usar las siguientes instrucciones regex: ? Sustituye {0,1} [1-9][0-9]?[0-9]?[a-z]? El único elemento obligatorio de este patrón de búsqueda es un número del “1” al “9”. Pueden estar seguidos opcionalmente tanto por dos dígitos del “0” al “9”, como por una letra cualquiera. Todas las opciones posibles se marcan con el signo de interrogación visto arriba. Aunque la construcción de números de tres dígitos con letras adicionales resulta bastante clara, los números de hasta diez dígitos tienen un aspecto bastante diferente. En este caso recomendamos utilizar las llaves tal y como se muestra en la siguiente expresión regular: [1-9][0-9]{,9}[a-z]? Haciendo otra modificación al ejemplo realizado anteriormente: [+-]?[$Ç]?\d{3}\s[,]\d{3}[,]\s\d[3][.]\s\d{2} … -$123 , 456 , 789 . 89 -123 , 456 , 789 . 89 $123 , 456 , 789 . 89 123 , 456 , 789 . 89 Clases de símbolos predefinidas La sintaxis de expresiones regulares de Java tiene algunas clases de símbolos predefinidas que puede utilizar. Por ejemplo, la clase de símbolo \ d coincide con cualquier dígito, la clase de símbolo \ s coincide con cualquier símbolo de espacio en blanco y el símbolo \ w coincide con cualquier símbolo de palabra. Las clases de símbolos predefinidas no tienen que estar entre corchetes, pero puede hacerlo si desea combinarlas. Aquí están algunos ejemplos: \d [\d\s] El primer ejemplo coincide con cualquier símbolo de dígito. El segundo ejemplo coincide con cualquier dígito o espacio en blanco. Las clases de símbolos predefinidas se enumeran en una tabla más adelante en este texto. Comparadores de límites La sintaxis también incluye comparadores para hacer coincidir límites, como límites entre palabras, el principio y el final del texto de entrada, etc. Por ejemplo, \w coincide con los límites entre palabras, ^ coincide con el comienzo de una línea y $ coincide con el final de una línea. Aquí hay un ejemplo de comparador de límites: ^Esta es una simple linea$ Otros Cuantificadores Como mostramos anteriormente con las llaves y el signo de interrogación, los cuantificadores son signos que se ponen detrás de un determinado patrón de búsqueda, y que indican las veces que debe aparecer en el texto para ajustarse al patrón. El símbolo * es un cuantificador que significa "cero o más veces". {0, } También hay un cuantificador + que significa “una o más veces”, {1, } y algunos otros que puede ver en la tabla de cuantificadores más adelante en este texto. \d+ … 1, 12, 123, 1234, … \* , 1, 2, 3, 4, 5, … Los cuantificadores pueden ser "reacios", "codiciosos" o "posesivos". Un cuantificador reacio coincidirá lo menos posible con el texto de entrada. Un cuantificador codicioso coincidirá tanto como sea posible con el texto de entrada. Un cuantificador posesivo coincidirá tanto como sea posible, incluso si hace que el resto de la expresión no coincida con nada y que la expresión falle al encontrar una coincidencia. Ilustraré la diferencia entre cuantificadores reacios, codiciosos y posesivos con un ejemplo. Aquí hay un texto de entrada: John went for a walk, and John fell down, and John hurt his knee. Luego, observe la siguiente expresión con un cuantificador reacio: John.*? Esta expresión coincidirá con la palabra John seguida de cero o más símbolos. significa "cualquier símbolo", y el * significa "cero o más veces". Los ? después de que * hace que * sea un cuantificador reacio. Al ser un cuantificador reacio, el cuantificador coincidirá lo menos posible, es decir, cero símbolos. Por tanto, la expresión encontrará la palabra John con cero símbolos después, 3 veces en el texto de entrada anterior. Si cambiamos el cuantificador a un cuantificador codicioso, la expresión se verá así: John.* El cuantificador codicioso coincidirá con tantos símbolos como sea posible. Ahora la expresión solo coincidirá con la primera aparición de John, y el cuantificador codicioso coincidirá con el resto de los símbolos del texto de entrada. Por lo tanto, solo se encuentra una coincidencia. Finalmente, cambiemos un poco la expresión para que contenga un cuantificador posesivo: John.*+hurt El + después del * lo convierte en un cuantificador posesivo. Esta expresión no coincidirá con el texto de entrada proporcionado anteriormente, incluso si las palabras John y hurt se encuentran en el texto de entrada. ¿Porqué es eso? Porque el. * + Es posesivo. En lugar de hacer coincidir tanto como sea posible para que la expresión coincida, como habría hecho un cuantificador codicioso, el cuantificador posesivo coincide tanto como sea posible, independientemente de si la expresión coincidirá o no. El. * + Coincidirá con todos los símbolos después de la primera aparición de John en el texto de entrada, incluida la palabra hurt. Por lo tanto, no queda ninguna palabra que coincidir, cuando el cuantificador posesivo ha reclamado su coincidencia. Si cambia el cuantificador a un cuantificador codicioso, la expresión coincidirá con el texto de entrada una vez. Así es como se ve la expresión con un cuantificador codicioso: John.*hurt Tendrá que jugar con los diferentes cuantificadores y tipos para comprender cómo funcionan. Consulte la tabla más adelante en este texto para obtener una lista completa de cuantificadores. Operadores lógicos La sintaxis de la expresión regular de Java también admite algunos operadores lógicos (y, o no). El operador y está implícito. Cuando escribe la expresión John, significa "J y o y h y n". El operador o es explícito y se escribe con |. Por ejemplo, la expresión John | hurt coincidirá con la palabra John o con la palabra hurt. Símbolos Símbolo x \\ \0n \0nn \0mnn \xhh \uhhhh \t \n \r \f \a \e Ocurrencia Se puede usar cualquier símbolo del alfabeto en lugar de x. El símbolo de barra invertida. Una sola barra invertida se utiliza como símbolo de escape junto con otros símbolos para señalar una coincidencia especial. De ahí la doble barra invertida para que coincida con un solo símbolo de barra invertida. El símbolo con valor octal 0n. n debe estar entre 0 y 7. El símbolo con valor octal 0nn. n debe estar entre 0 y 7. El símbolo con valor octal 0mnn. m debe estar entre 0 y 3, n debe estar entre 0 y 7. El símbolo con el valor hexadecimal 0xhh. El símbolo con el valor hexadecimal 0xhhhh. El símbolo de tabulación. El símbolo de nueva línea (salto de línea) (unicode: '\ u000A'). El símbolo de retorno de carro (unicode: '\ u000D'). El símbolo de “form-feed” (unicode: '\ u000C'). El símbolo de alerta (campana) (unicode: '\ u0007'). El símbolo escape (unicode: '\ u001B'). \cx El símbolo de control correspondiente ax. Clases de Símbolos Símbolo [abc] [^abc] [a-zA-Z] [a-d[m-p]] [a-z&&[def]] [a-z&&[^bc]] [a-z&&[^m-p]] Coincidencias Coincide con a, b o c. Esto se llama un conjunto simple y coincide con cualquiera de los símbolos del conjunto. Coincide con cualquier símbolo excepto a, b y c. Esta es una negación. Coincide con cualquier símbolo de la a a la z, o de la A a la Z, incluidos a, A, z y Z. Esto se denomina rango. Coincide con cualquier símbolo desde a hasta d, o desde m hasta p. A esto se le llama unión. Coincide con d, e o f. Esto se denomina intersección (aquí entre el rango a-z y los caracteres d e f). Coincide con todos los caracteres de la a a la z excepto by c. Esto se llama resta. Coincide con todos los símbolos de la a a la z excepto los caracteres de la m a la p. Esto también se llama resta. Clases de símbolos predefinidas Símbolo . \d \D \s \S \w \W Coincidencias Coincide con cualquier símbolo individual. Puede que coincida o no con los terminadores de línea, según los indicadores que se utilizaron para compilar el patrón. Coincide con cualquier símbolo digito [0-9] Coincide con cualquier símbolo no digito [^0-9] Coincide con cualquier carácter de espacio en blanco (espacio, tabulación, salto de línea, retorno de carro) Coincide con cualquier símbolo no espacio en blanco. Coincide con cualquier símbolo word. Coincide con cualquier símbolo no word. Cuantificadores Codiciosas X? Renuentes X?? Posesivas X?+ Coincidencias Coincide con X una vez, o no coincide (0 o 1 vez). X* X+ X{n} X{n,} X*? X+? X{n}? X{n,}? X*+ X++ X{n}+ X{n,}+ X{n, m) X{n, m)? X{n, m)+ Coincide con X cero o más veces. Coincide con X una o más veces. Coincide con X exactamente n veces. Coincide con X al menos n veces. Coincide con X al menos n veces, pero como máximo m veces. Evaluar cadenas que son un número real con signo con exponente e x10 1.0, +12.34e5, -100.456E-10, 023.00e+3, … import java.util.regex.Pattern; public class PatternMatchesExample { public static void main(String[] args) { String pattern = “[+-]?\\d+\\.\\d+[eE][+-]?\\d+”; String text = “z“; boolean matches = Pattern.matches(pattern, text); if(matches) { System.out.println(“Es un real con exponente”); } else{ System.out.println("No es un real con exponente“); } } }