JavaRush /Blog Java /Random-ES /RegEx: 20 breves pasos para dominar las expresiones regul...
Artur
Nivel 40
Tallinn

RegEx: 20 breves pasos para dominar las expresiones regulares. parte 4

Publicado en el grupo Random-ES
RegEx: 20 breves pasos para dominar las expresiones regulares. Parte 1 RegEx: 20 pasos cortos para dominar las expresiones regulares. Parte 2 20 pasos cortos para dominar las expresiones regulares. Parte 3 Esta parte final, en el medio, abordará cosas que utilizan principalmente los maestros de expresiones regulares. Pero el material de las partes anteriores te resultó fácil, ¿verdad? ¡Esto significa que puedes manejar este material con la misma facilidad! Original aquí RegEx: 20 breves pasos para dominar las expresiones regulares.  Parte 4 - 1 <h2>Paso 16: grupos sin capturar (?:)</h2> RegEx: 20 breves pasos para dominar las expresiones regulares.  Parte 4 - 2En los dos ejemplos del paso anterior, estábamos capturando texto que realmente no necesitábamos. En la tarea Tamaños de archivo, capturamos los espacios antes del primer dígito de los tamaños de archivo y, en la tarea CSV, capturamos las comas entre cada token. No necesitamos capturar estos caracteres, pero sí usarlos para estructurar nuestra expresión regular. Estas son opciones ideales para usar un grupo sin capturar (?:). Un grupo que no captura hace exactamente lo que parece: permite agrupar y usar caracteres en expresiones regulares, pero no los captura en un grupo numerado:
patrón: (?:")([^"]+)(?:") 
cadena: solo quiero "el texto dentro de estas comillas" .
coincidencias:             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
grupo:                 1111111111111111111111111111    
( Ejemplo ) La expresión regular ahora coincide con el texto citado así como con los propios caracteres de las comillas, pero el grupo de captura solo capturó el texto citado. ¿Por qué deberíamos hacer ésto? El punto es que la mayoría de los motores de expresiones regulares le permiten recuperar texto de grupos de captura definidos en sus expresiones regulares. Si podemos recortar los caracteres adicionales que no necesitamos sin incluirlos en nuestros grupos de captura, será más fácil analizar y manipular el texto más adelante. A continuación se explica cómo limpiar el analizador CSV del paso anterior:
patrón: (?:^|,)\s*(?:\"([^",]*)\"|([^", ]*)) 
cadena:   a , " b ", " cd ", e , f , " gh ", dfgi ,, k , "", l 
coincide con: ^ ^ ^^^ ^ ^ ^^^ ^^^^ ^ ^ 
grupo:    2 1 111 2 2 111 2222 2 2    
( Ejemplo ) Hay algunas cosas que <mark>observar aquí:</mark> Primero, ya no capturamos comas desde que cambiamos el grupo de captura (^|,)a un grupo que no captura (?:^|,). En segundo lugar, anidamos el grupo de captura dentro del grupo de no captura. Esto es útil cuando, por ejemplo, necesitas que un grupo de personajes aparezca en un orden específico, pero solo te interesa un subconjunto de esos personajes. En nuestro caso, necesitábamos caracteres sin comillas ni comas [^",]*para que aparecieran entre comillas, pero en realidad no necesitábamos los caracteres entre comillas en sí, por lo que no era necesario capturarlos. Finalmente, <mark>tenga en cuenta</mark> que en el ejemplo anterior también hay una coincidencia de longitud cero entre los caracteres ky l. Las comillas ""son la subcadena buscada, pero no hay caracteres entre las comillas, por lo que la subcadena coincidente no contiene caracteres (longitud cero). <h3>¿Consolidamos nuestros conocimientos? Aquí hay dos tareas y media que nos ayudarán con esto:</h3> Usando grupos que no capturan (y grupos que capturan, clases de caracteres, etc.), escribe una expresión regular que capture solo tamaños de archivos con el formato adecuado en la línea. abajo :
patrón:
cadena:   6.6KB 1..3KB 12KB 5G 3.3MB KB .6.2TB 9MB .
coincidencias: ^^^^^ ^^^^^ ^^^^^^ ^^^^ 
grupo:    11111 1111 11111 111    
( Solución ) Las etiquetas de apertura HTML comienzan con <y terminan con >. Las etiquetas de cierre HTML comienzan con una secuencia de caracteres </y terminan con el carácter >. El nombre de la etiqueta está contenido entre estos caracteres. ¿Puedes escribir una expresión regular para capturar solo los nombres de las siguientes etiquetas? (Es posible que pueda resolver este problema sin utilizar grupos que no capturen. ¡Intente resolver esto de dos maneras! Una con grupos y otra sin ellos).
patrón:
cadena:   <p> </span> <div> </kbd> <link> 
coincidencias: ^^^ ^^^^^^ ^^^^^ ^^^^^^ ^^^^^^ 
grupo:    1 1111 111 111 1111    
( Solución usando grupos sin captura ) ( Solución sin usando grupos sin captura ) <h2>Paso 17: Vínculos de retroceso \Ny grupos de captura con nombre</h2> RegEx: 20 breves pasos para dominar las expresiones regulares.  Parte 4 - 3Aunque te advertí en la introducción que intentar crear un analizador HTML usando expresiones regulares generalmente lleva a la angustia, este último ejemplo es una buena transición hacia otra característica (a veces) útil de la mayoría de las expresiones regulares: las referencias inversas. Los vínculos de retroceso son como grupos repetidos en los que puedes intentar capturar el mismo texto dos veces. Pero difieren en un aspecto importante: sólo capturarán el mismo texto, carácter por carácter. Mientras que un grupo repetido nos permitirá capturar algo como esto:
patrón: (he(?:[az])+) 
cadena:   heyabcdefg hey heyo heamarillo heyyyyyyyyy 
coincide con: ^^^^^^^^^^ ^^^ ^^^^ ^^^^^^^^ ^^^ ^^^^^^^^ 
grupo:    1111111111 111 1111 11111111 11111111111    
( Ejemplo ) ... entonces el vínculo de retroceso coincidirá solo con esto:
patrón: (he([az])(\2+)) 
cadena: heyabcdefg hey heyo heamarillo heyyyyyyyyy 
coincidencias:                              ^^^^^^^^^^^ 
grupo:                                 11233333333    
( Ejemplo ) Los grupos de captura repetidos son útiles cuando desea hacer coincidir el mismo patrón repetidamente, mientras que los vínculos de retroceso son buenos cuando desea hacer coincidir el mismo texto. Por ejemplo, podríamos usar un vínculo de retroceso para intentar encontrar etiquetas HTML de apertura y cierre coincidentes:
patrón: <(\w+)[^>]*>[^<]+<\/\1> 
cadena:   <span style="color: red">hey</span> 
coincidencias: ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
grupo:    1111    
( Ejemplo ) <mark>Tenga en cuenta</mark> que este es un ejemplo extremadamente simplificado y le recomiendo encarecidamente que no intente escribir un analizador HTML basado en expresiones regulares. Esta es una sintaxis muy compleja y lo más probable es que te enferme. Los grupos de captura con nombre son muy similares a los vínculos de retroceso, por lo que los cubriré brevemente aquí. La única diferencia entre referencias retrospectivas y un grupo de captura con nombre es que... un grupo de captura con nombre tiene un nombre:
patrón: <(?<tag>\w+)[^>]*>[^<]+<\/(?P=tag)></tag> 
cadena:   <span style="color: red">hey< /span> 
coincidencias: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
grupo:    1111    
( Ejemplo ) Puede crear un grupo de captura con nombre usando la sintaxis (?<nombre>...) o (?'nombre'...) (expresión regular compatible con .NET) o con esta sintaxis (?P<nombre>. ..) o (?P'name'...) (expresión regular compatible con Python). Dado que estamos usando PCRE (Perl Compatible Regular Expression) que admite ambas versiones, podemos usar cualquiera de ellas aquí. (Java 7 copió la sintaxis de .NET, pero solo la versión de corchetes angulares. Nota del traductor) Para repetir un grupo de captura con nombre más adelante en una expresión regular, usamos \<kname> o \k'name' (.NET) o (? P= nombre) (Python). Nuevamente, PCRE admite todas estas opciones diferentes. Puede leer más sobre los grupos de captura con nombre aquí , pero esto es la mayor parte de lo que realmente necesita saber sobre ellos. <h3>Tarea para ayudarnos:</h3> Usar vínculos de retroceso para ayudarme a recordar... ummm... el nombre de esta persona.
patrón:
cadena: "Hola, mi nombre es Joe". [más tarde] "¿Cómo se llama ese tipo? ¿Joe ?"
coincidencias:        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ 
grupo:                  111    
( Solución ) <h2>Paso 18: mirar hacia adelante y hacia atrás</h2> RegEx: 20 breves pasos para dominar las expresiones regulares.  Parte 4 - 4Ahora profundizaremos en algunas de las características avanzadas de las expresiones regulares. Utilizo todo hasta el paso 16 con bastante frecuencia. Pero estos últimos pasos son sólo para personas que usan expresiones regulares muy en serio para hacer coincidir expresiones muy complejas. En otras palabras, maestros de las expresiones regulares. "Mirar hacia adelante" y "Mirar hacia atrás" pueden parecer bastante complicados, pero en realidad no lo son demasiado. Le permiten hacer algo similar a lo que hicimos anteriormente con los grupos que no capturan: verificar si hay algún texto inmediatamente antes o inmediatamente después del texto real que queremos hacer coincidir. Por ejemplo, supongamos que queremos hacer coincidir solo los nombres de las cosas que le gustan a la gente, pero solo si les entusiasma (solo si terminan su oración con un signo de exclamación). Podríamos hacer algo como:
patrón: (\w+)(?=!) 
cadena: Me gusta el escritorio. Aprecio la grapadora. Amo las lamparas !
coincidencias:                                           ^^^^ 
grupo:                                              1111    
( Ejemplo ) Puede ver cómo el grupo de captura anterior (\w+), que generalmente coincide con cualquiera de las palabras del pasaje, solo coincide con la palabra lámpara. La anticipación positiva (?=!)significa que solo podemos hacer coincidir secuencias que terminan en !pero en realidad no hacemos coincidir el carácter del signo de exclamación en sí. Esta es una distinción importante porque con los grupos que no capturan hacemos coincidir al personaje pero no lo capturamos. Con las miradas hacia adelante y hacia atrás, usamos un carácter para construir nuestra expresión regular, pero luego ni siquiera lo comparamos con él mismo. Podemos igualarlo más adelante en nuestra expresión regular. Hay cuatro tipos de anticipaciones y retrospectivas: anticipaciones positivas (?=...), anticipaciones negativas (?!...), anticipaciones positivas (?<=...) y anticipaciones negativas (?<!. ..) . Hacen lo que parecen: la búsqueda anticipada y posterior positiva permiten que el motor de expresiones regulares continúe coincidiendo solo cuando el texto contenido en la búsqueda anticipada/posterior realmente coincide. La búsqueda hacia adelante y hacia atrás negativa hace lo contrario: permiten que la expresión regular coincida solo cuando el texto contenido en la búsqueda hacia delante/hacia atrás no coincide. Por ejemplo, queremos hacer coincidir los nombres de los métodos solo en una cadena de secuencias de métodos, no en el objeto sobre el que operan. En este caso, cada nombre de método debe ir precedido de un .. Una expresión regular que utiliza una simple mirada atrás puede ayudar aquí:
patrón: (?<=\.)(\w+) 
cadena: myArray. flatMap.aggregate.summarise.print !
coincidencias:         ^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^ 
grupo:            1111111 111111111 111111111 11111    
( Ejemplo ) En el texto anterior, hacemos coincidir cualquier secuencia de caracteres de palabras \w+, pero solo si están precedidos por el carácter .. Podríamos lograr algo similar usando grupos que no capturan, pero el resultado es un poco más complicado:
patrón: (?:\.)(\w+) 
cadena: myArray .flatMap.aggregate.summarise.print !
coincidencias:        ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^ 
grupo:            1111111 111111111 111111111 11111    
( Ejemplo ) Aunque es más corto, coincide con caracteres que no necesitamos. Si bien este ejemplo puede parecer trivial, las miradas hacia adelante y hacia atrás realmente pueden ayudarnos a limpiar nuestras expresiones regulares. <h3>¡Quedan muy pocos hasta el final! Las dos tareas siguientes nos acercarán un paso más a ello:</h3> La búsqueda hacia atrás negativa (?<!...) permite que el motor de expresiones regulares continúe intentando encontrar una coincidencia sólo si el texto contenido dentro de la búsqueda hacia atrás negativa no lo es. se muestra hasta el resto del texto, con el que necesita encontrar una coincidencia. Por ejemplo, podríamos usar una expresión regular para hacer coincidir solo los apellidos de las mujeres que asisten a una conferencia. Para hacer esto, nos gustaría asegurarnos de que el apellido de la persona no esté precedido por un Mr.. ¿Puedes escribir una expresión regular para esto? (Se puede suponer que los apellidos tienen al menos cuatro caracteres).
patrón:
cadena: Sr. marrón, Sra. Smith , Sra. Jones , la señorita Daisy , el Sr. Verde
coincidencias:                ^^^^^ ^^^^^ ^^^^^ 
grupo:                   11111 11111 11111    
( Solución ) Digamos que estamos limpiando una base de datos y tenemos una columna de información que representa porcentajes. Desafortunadamente, algunas personas escribieron números como valores decimales en el rango [0.0, 1.0], mientras que otras escribieron porcentajes en el rango [0.0%, 100.0%] y otros escribieron valores porcentuales pero olvidaron el signo de porcentaje literal %. Usando anticipación negativa (?!...), ¿puedes marcar solo aquellos valores que deberían ser porcentajes pero a los que les faltan dígitos %? Estos deben ser valores estrictamente superiores a 1,00, pero sin final %. (Ningún número puede contener más de dos dígitos antes o después del punto decimal). <mark>Tenga en cuenta</mark> que esta solución es extremadamente difícil . Si puedes resolver este problema sin mirar mi respuesta, ¡entonces ya tienes grandes habilidades con las expresiones regulares!
patrón:
cadena: 0,32 100,00 5,6 0,27 98% 12,2% 1,01 0,99% 0,99 13,13 1,10 
coincidencias:      ^^^^^^ ^^^ ^^^^ ^^^^^ ^^^^ 
grupo:         111111 111 1111 11111 1111    
( Solución ) <h2>Paso 19: Condiciones en expresiones regulares</h2> RegEx: 20 breves pasos para dominar las expresiones regulares.  Parte 4 - 5Hemos llegado al punto en el que la mayoría de la gente ya no utilizará expresiones regulares. Probablemente hemos cubierto el 95% de los casos de uso de expresiones regulares simples, y todo lo que se hace en los pasos 19 y 20 generalmente se realiza mediante un lenguaje de manipulación de texto con más funciones como awk o sed (o un lenguaje de programación de propósito general). Dicho esto, sigamos adelante, para que sepas lo que realmente puede hacer una expresión regular. Aunque las expresiones regulares no son Turing completas , algunos motores de expresiones regulares ofrecen características que son muy similares a un lenguaje de programación completo. Una de esas características es la "condición". Los condicionales de expresiones regulares permiten declaraciones if-then-else, donde la rama elegida está determinada por "mirar hacia adelante" o "mirar hacia atrás" que aprendimos en el paso anterior. Por ejemplo, es posible que desee hacer coincidir solo las entradas válidas en una lista de fechas:
patrón: (?<=Feb )([1-2][0-9])|(?<=Mar )([1-2][0-9]|3[0-1]) 
cadena: Fechas trabajadas : 28 de febrero , 29 de febrero , 30 de febrero, 30 de marzo , 31 de marzo  
partidos:                   ^^ ^^ ^^ ^^ 
grupo:                      11 11 22 22    
( Ejemplo ) <mark>Tenga en cuenta</mark> que los grupos anteriores también están indexados por mes. Podríamos escribir una expresión regular para los 12 meses y capturar solo fechas válidas, que luego se combinarían en grupos indexados por mes del año. Lo anterior utiliza una especie de estructura tipo if que solo buscará coincidencias en el primer grupo si "Feb" precede a un número (y de manera similar para el segundo). Pero, ¿qué pasaría si sólo quisiéramos utilizar el procesamiento especial para febrero? Algo así como "si el número está precedido por "Feb", haz esto; de lo contrario, haz esta otra cosa". Así es como lo hacen los condicionales:
patrón: (?(?<=Feb )([1-2][0-9])|([1-2][0-9]|3[0-1])) 
cadena: Fechas de trabajo: 28 de febrero , 29 de febrero , 30 de febrero, 30 de marzo , 31 de marzo  
partidos:                   ^^ ^^ ^^ ^^ 
grupo:                      11 11 22 22    
( Ejemplo ) La estructura if-then-else se parece a (?(If)then|else), donde (if) se reemplaza por "mirar hacia adelante" o "mirar hacia atrás". En el ejemplo anterior, (if) se escribe como (?<=Feb). Puedes ver que coincidimos con fechas mayores que 29, pero solo si no siguieron a "Feb". El uso de búsqueda hacia atrás en expresiones condicionales es útil si desea asegurarse de que la coincidencia esté precedida por algún texto. Los condicionales positivos de anticipación pueden resultar confusos porque la condición en sí no coincide con ningún texto. Entonces, si desea que la condición if alguna vez tenga un valor, debe ser comparable a la búsqueda anticipada como se muestra a continuación:
patrón: (?(?=exacto)exacto|otro)dos 
cadenas: exacto más exactamentedos más dos  
coincidencias:            ^^^^^^^ ^^^^^^
( Ejemplo ) Esto significa que los condicionales positivos de anticipación son inútiles. Verifica si ese texto está al frente y luego proporciona un patrón coincidente a seguir cuando lo esté. La expresión condicional no nos ayuda en absoluto aquí. También puedes simplemente reemplazar lo anterior con una expresión regular más simple:
patrón: (?:exacto|otro)dos 
cadenas: exacto otra cosa exactamente dos otras  
coincidencias:            ^^^^^^^ ^^^^^^
( Ejemplo ) Entonces, la regla general para las expresiones condicionales es: probar, probar y probar nuevamente. De lo contrario, las soluciones que crees que son obvias fallarán de la manera más interesante e inesperada :) <h3>Aquí llegamos al último bloque de tareas que nos separa del vigésimo paso final:</h3> Escribe una expresión regular que utiliza una expresión condicional negativa de anticipación para comprobar si la siguiente palabra comienza con una letra mayúscula. Si es así, tome solo una letra mayúscula y luego las minúsculas. Si no es así, tome los caracteres de las palabras.
patrón:
cadena:   Jones Smith 9sfjn Hobbes 23r4tgr9h CSV Csv vVv 
coincidencias: ^^^^^ ^^^^^ ^^^^^ ^^^^^^ ^^^^^^^^^ ^^^ ^^^ 
grupo:    22222 22222 11111 222222 111111111 222 111    
( Solución ) Escriba una expresión condicional negativa de búsqueda hacia atrás que capture texto ownssólo si no está precedido por texto cl, y que capture texto oudssólo cuando esté precedido por texto cl. (Un ejemplo un poco artificial, pero ¿qué puedes hacer...?)
patrón:
cadena: Esos payasos poseen algunas nubes . ouds.
coincidencias:              ^^^^ ^^^^   
( Solución ) <h2>Paso 20: recursión y estudio adicional</h2> RegEx: 20 breves pasos para dominar las expresiones regulares.  Parte 4 - 6De hecho, hay muchas cosas que se pueden incluir en una introducción de 20 pasos a cualquier tema, y ​​las expresiones regulares no son una excepción. Existen muchas implementaciones y estándares diferentes para expresiones regulares que se pueden encontrar en Internet. Si desea obtener más información, le sugiero que visite el maravilloso sitio regularexpressions.info , es una referencia fantástica y ciertamente aprendí mucho sobre las expresiones regulares desde allí. Lo recomiendo mucho, así como regex101.com para probar y publicar sus creaciones. En este paso final, le brindaré un poco más de conocimiento sobre las expresiones regulares, es decir, cómo escribir expresiones recursivas. Las recursiones simples son bastante simples, pero pensemos en lo que eso significa en el contexto de una expresión regular. La sintaxis de la recursividad simple en una expresión regular se escribe así: (?R)?. Pero, por supuesto, esta sintaxis debe aparecer dentro de la propia expresión. Lo que haremos será anidar la expresión dentro de sí misma, un número arbitrario de veces. Por ejemplo:
patrón: (hey(?R)?oh) 
cadena:   heyoh heyyoh heyheyohoh hey oh heyhey hey heyheyohoh  
coincidencias: ^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ 
grupo:    11111 1111111111 1111111111    
( Ejemplo ) Dado que la expresión anidada es opcional ( (?R)seguida ?), la coincidencia más simple es simplemente ignorar la recursividad por completo. Entonces, heyy luego ohcoincide con ( heyoh). Para hacer coincidir cualquier expresión más compleja que esta, debemos encontrar la subcadena coincidente anidada dentro de sí misma en el punto de la expresión donde insertamos (?R)la secuencia. En otras palabras, podríamos encontrar heyheyohoh o heyheyheyohohoh, etc. Una de las mejores cosas de estas expresiones anidadas es que, a diferencia de las referencias retrospectivas y los grupos de captura con nombre, no lo restringen al texto exacto con el que coincidió anteriormente, carácter por carácter. Por ejemplo:
patrón: ([Hh][Ee][Yy](?R)?oh) 
cadena:   heyoh heyyoh hEyHeYohoh hey oh heyhey hEyHeYHEyohohoh  
coincide con: ^^^^^ ^^^^^^^^^^ ^^^^^ ^^^^^^^^^^ 
grupo:    11111 1111111111 111111111111111    
( Ejemplo ) Puede imaginar que el motor de expresiones regulares literalmente copia y pega su expresión regular en sí mismo una cantidad arbitraria de veces. Por supuesto, esto significa que a veces puede que no haga lo que esperabas:
patrón: ((?:\(\*)[^*)]*(?R)?(?:\*\))) 
cadena: (* comentario (* anidado *) no *)
coincidencias:            ^^^^^^^^^^^^ 
grupo:               111111111111    
( Ejemplo ) ¿Puede decirnos por qué esta expresión regular solo capturó el comentario anidado y no el comentario externo? Una cosa es segura: cuando escriba expresiones regulares complejas, pruébelas siempre para asegurarse de que funcionen como cree. Este rally de alta velocidad por las carreteras de las expresiones regulares ha llegado a su fin. Espero que hayas disfrutado de este viaje. Bueno, y para terminar, dejaré aquí, como prometí al principio, varios enlaces útiles para un estudio más profundo del material:
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION