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 3

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: En esta parte pasaremos a cosas que son un poco más complejas. Pero dominarlos, como antes, no será difícil. Repito que RegEx es en realidad más fácil de lo que parece al principio y no es necesario ser un genio para dominarlo y comenzar a usarlo en la práctica. El original en inglés de este artículo está aquí. . 20 breves pasos para dominar las expresiones regulares.  Parte 3 - 1

Paso 11: paréntesis()como grupos de captura

20 breves pasos para dominar las expresiones regulares.  Parte 3 - 2En el último problema, buscamos diferentes tipos de valores enteros y valores numéricos de punto flotante (punto). Pero el motor de expresiones regulares no diferenciaba entre estos dos tipos de valores, ya que todo estaba capturado en una gran expresión regular. Podemos decirle al motor de expresiones regulares que diferencie entre diferentes tipos de coincidencias si encerramos nuestros minipatrones entre paréntesis:
patrón: ([AZ])|([az]) 
cadena:   El actual presidente de Bolivia es Evo Morales .
coincidencias: ^^^ ^^^^^^^ ^^^^^^^^^ ^^ ^^^^^^^ ^^ ^^^ ^^^^^^^ 
grupo:    122 2222222 122222222 22 1222222 22 122 1222222  
( Ejemplo ) La expresión regular anterior define dos grupos de captura que se indexan comenzando en 1. El primer grupo de captura coincide con cualquier letra mayúscula y el segundo grupo de captura coincide con cualquier letra minúscula. Usando el signo 'o' |y paréntesis() como grupo de captura, podemos definir una única expresión regular que coincida con varios tipos de cadenas. Si aplicamos esto a nuestra expresión regular de búsqueda larga/flotante de la parte anterior del artículo, entonces el motor de expresiones regulares capturará las coincidencias correspondientes en los grupos apropiados. Al verificar con qué grupo coincide una subcadena, podemos determinar inmediatamente si es un valor flotante o un valor largo:
patrón: (\d*\.\d+[fF]|\d+\.\d*[fF]|\d+[fF])|(\d+[lL]) 
cadena:   42L 12 x 3.4f 6l 3.3 0F LF .2F 0.
partidos: ^^^ ^^^^ ^^ ^^ ^^^ 
grupo:    222 1111 22 11 111  
( Ejemplo ) Esta expresión regular es bastante compleja y, para comprenderla mejor, analicémosla y observemos cada uno de estos patrones:
( // coincide con cualquier subcadena "flotante"
  \d*\.\d+[fF]
  |
  \d+\.\d*[fF]
  |
  \d+[fF]
)
| //O
( // coincide con cualquier subcadena "larga"
  \d+[ll]
)
Firmar |y capturar grupos entre paréntesis() nos permiten hacer coincidir diferentes tipos de subcadenas. En este caso, estamos haciendo coincidir números de coma flotante "flotantes" o enteros largos "largos".
(
  \d*\.\d+[fF] // 1+ dígitos a la derecha del punto decimal
  |
  \d+\.\d*[fF] // 1+ dígitos a la izquierda del punto decimal
  |
  \d+[fF] // sin punto, solo 1+ dígitos
)
|
(
  \d+[lL] // sin punto, solo 1+ dígitos
)
En el grupo de captura "flotante", tenemos tres opciones: números con al menos 1 dígito a la derecha del punto decimal, números con al menos 1 dígito a la izquierda del punto decimal y números sin punto decimal. Cualquiera de ellos es "flotante" siempre que tenga las letras "f" o "F" al final. Dentro del grupo de captura "larga" sólo tenemos una opción: debemos tener 1 o más dígitos seguidos del carácter "l" o "L". El motor de expresiones regulares buscará estas subcadenas en una cadena determinada y las indexará en el grupo de captura apropiado. notaque no estamos haciendo coincidir ninguno de los números a los que no se les ha agregado "l", "L", "f" o "F". ¿Cómo deberían clasificarse estos números? Bueno, si tienen un punto decimal, el lenguaje Java por defecto es "doble". De lo contrario deben ser "int".

Consolidemos lo que hemos aprendido con un par de acertijos:

Agregue dos grupos de captura más a la expresión regular anterior para que también clasifique números dobles o int. (Esta es otra pregunta complicada, no se desanime si lleva un tiempo; como último recurso, consulte mi solución).
patrón:
cadena:   42L 12 x 3.4f 6l 3.3 0F LF .2F 0. 
coincidencias: ^^^ ^^ ^^^^ ^^ ^^^ ^^ ^^^ ^^ 
grupo:    333 44 1111 33 222 11 111 22
( Solución ) El siguiente problema es un poco más sencillo. Utilice grupos de captura entre corchetes (), el signo "o" |y rangos de caracteres para ordenar las siguientes edades: "es legal beber en EE. UU.". (>= 21) y "no está permitido beber en EE. UU." (<21):
patrón:
cadena:   7 10 17 18 19 20 21 22 23 24 30 40 100 120 
coincidencias: ^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^^ ^^^ 
grupo:    2 22 22 22 22 22 11 11 11 11 11 11 111 111 
( Solución )

Paso 12: Identifique primero coincidencias más específicas

20 breves pasos para dominar las expresiones regulares.  Parte 3 - 3Es posible que haya tenido algunos problemas con la última tarea si intentó definir a los "bebedores legales" como el primer grupo de captura en lugar del segundo. Para entender por qué, veamos otro ejemplo. Supongamos que queremos registrar por separado los apellidos que contienen menos de 4 caracteres y los apellidos que contienen 4 o más caracteres. Démosle nombres más cortos al primer grupo de captura y veamos qué sucede:
patrón: ([AZ][az]?[az]?)|([AZ][az][az][az]+) 
cadena:   Kim Job s Xu Clo yd Moh r Ngo Roc k.
coincidencias: ^^^ ^^^ ^^ ^^^ ^^^ ^^^ ^^^ 
grupo:    111 111 11 111 111 111 111   
( Ejemplo ) De forma predeterminada, la mayoría de los motores de expresiones regulares utilizan coincidencias codiciosas con los caracteres básicos que hemos visto hasta ahora. Esto significa que el motor de expresiones regulares capturará el grupo más largo definido lo antes posible en la expresión regular proporcionada. Entonces, aunque el segundo grupo anterior podría capturar más caracteres en nombres como "Jobs" y "Cloyd", por ejemplo, pero dado que los primeros tres caracteres de esos nombres ya fueron capturados por el primer grupo de captura, no pueden ser capturados nuevamente por el segundo. . Ahora hagamos una pequeña corrección: simplemente cambie el orden de los grupos de captura, colocando primero el grupo más específico (más largo):
patrón: ([AZ][az][az][az]+)|([AZ][az]?[az]?) 
cadena:   Kim Jobs Xu Cloyd Mohr Ngo Rock .
coincidencias: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^ 
grupo:    222 1111 22 11111 1111 222 1111    
( Ejemplo )

Tarea... esta vez solo una :)

Un patrón "más específico" casi siempre significa "más largo". Digamos que queremos encontrar dos tipos de "palabras": primero las que comienzan con vocales (más específicamente), luego las que no comienzan con vocales (cualquier otra palabra). Intente escribir una expresión regular para capturar e identificar cadenas que coincidan con estos dos grupos. (Los grupos siguientes están escritos con letras en lugar de numerados. Debe determinar qué grupo debe corresponder al primero y cuál al segundo).
patrón:
cadena:   pds6f uub 24r2gp ewqrty l ui_op 
coincidencias: ^^^^^ ^^^ ^^^^^^ ^^^^^^ ^ ^^^^^ 
grupo:    NNNNN VVV NNNNNN VVVVVV N VVVVV
( Solución ) En general, cuanto más precisa sea su expresión regular, más larga terminará. Y cuanto más preciso sea, menos probable será que capture algo que no necesita. Entonces, si bien pueden parecer aterradores, las expresiones regulares más largas ~= mejores expresiones regulares. Desafortunadamente .

Paso 13: llaves {}para un número específico de repeticiones

20 breves pasos para dominar las expresiones regulares.  Parte 3 - 4En el ejemplo con apellidos del paso anterior, teníamos 2 grupos casi repetidos en un patrón:
patrón: ([AZ][az][az][az]+)|([AZ][az]?[az]?) 
cadena:   Kim Jobs Xu Cloyd Mohr Ngo Rock .
coincidencias: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^ 
grupo:    222 1111 22 11111 1111 222 1111    
Para el primer grupo, necesitábamos apellidos con cuatro o más letras. El segundo grupo tuvo que capturar apellidos con tres letras o menos. ¿Existe alguna forma más sencilla de escribir esto que repetir estos [a-z]grupos una y otra vez? Existe si usa llaves para esto {}. Las llaves {}nos permiten especificar el número mínimo y (opcionalmente) máximo de coincidencias del personaje o grupo de captura anterior. Hay tres casos de uso {}:
{X} // coincide exactamente X veces
{X,} // coincide >= X veces
{X,Y} // coincide >= X y <= Y veces
A continuación se muestran ejemplos de estas tres sintaxis diferentes:
patrón: [az]{11} 
cadena:   humuhumunuk unukuapua'a.
coincidencias: ^^^^^^^^^^^   
( Ejemplo )
patrón: [az]{18,} 
cadena:   humuhumunukunukuapua 'a.
coincidencias: ^^^^^^^^^^^^^^^^^^^^^    
( Ejemplo )
patrón: [az]{11,18} 
cadena:   humuhumunukunukuap ua'a.
coincidencias: ^^^^^^^^^^^^^^^^^^    
( Ejemplo ) Hay varios puntos a tener en cuenta en los ejemplos anteriores.nota:. Primero, usando la notación {X}, el carácter o grupo anterior coincidirá exactamente con ese número (X) veces. Si hay más caracteres en la "palabra" (que el número X) que podrían coincidir con el patrón (como se muestra en el primer ejemplo), no se incluirán en la coincidencia. Si el número de caracteres es menor que X, la coincidencia completa fallará (intente cambiar 11 a 99 en el primer ejemplo). En segundo lugar, las notaciones {X,} y {X,Y} son codiciosas. Intentarán hacer coincidir tantos caracteres como sea posible sin dejar de satisfacer la expresión regular dada. Si especifica {3,7}, se pueden hacer coincidir de 3 a 7 caracteres y, si los siguientes 7 caracteres son válidos, se harán coincidir los 7 caracteres. Si especifica {1,} y los siguientes 14.000 caracteres coinciden, esos 14.000 caracteres se incluirán en la cadena correspondiente. ¿Cómo podemos usar este conocimiento para reescribir nuestra expresión anterior? La mejora más sencilla podría ser reemplazar los grupos vecinos [a-z]con [a-z]{N}, donde N se elige en consecuencia:
patrón: ([AZ][az]{2}[az]+)|([AZ][az]?[az]?)  
...pero eso no mejora mucho las cosas. Mire el primer grupo de captura: tenemos [a-z]{2}(que coincide exactamente con 2 letras minúsculas) seguido de [a-z]+(que coincide con 1 o más letras minúsculas). Podemos simplificar esto pidiendo 3 o más letras minúsculas usando llaves:
patrón: ([AZ][az]{3,})|([AZ][az]?[az]?) 
El segundo grupo de captura es diferente. No necesitamos más de tres caracteres en estos apellidos, lo que significa que tenemos un límite superior, pero nuestro límite inferior es cero:
patrón: ([AZ][az]{3,})|([AZ][az]{0,2}) 
La especificidad siempre es mejor cuando se usan expresiones regulares, por lo que sería prudente detenerse allí, pero no puedo evitar notar que estos dos rangos de caracteres ( [AZ]y [az]) uno al lado del otro parecen casi una clase de "carácter de palabra", \w( [A-Za-z0-9_]) . Si estuviéramos seguros de que nuestros datos solo contienen apellidos bien formateados, entonces podríamos simplificar nuestra expresión regular y escribir simplemente:
patrón: (\w{4,})|(\w{1,3}) 
El primer grupo captura cualquier secuencia de 4 o más "caracteres de palabra" ( [A-Za-z0-9_]), y el segundo grupo captura cualquier secuencia de 1 a 3 "caracteres de palabra" (inclusive). esto funcionara?
patrón: (\w{4,})|(\w{1,3}) 
cadena:   Kim Jobs Xu Cloyd Mohr Ngo Rock .
coincidencias: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^ 
grupo:    222 1111 22 11111 1111 222 1111    
( Ejemplo ) ¡Funcionó! ¿Qué tal este enfoque? Y es mucho más limpio que nuestro ejemplo anterior. Dado que el primer grupo de captura coincide con todos los apellidos con cuatro o más caracteres, incluso podríamos cambiar el segundo grupo de captura a simplemente \w+, ya que esto nos permitiría capturar todos los apellidos restantes (con 1, 2 o 3 caracteres):
patrón: (\w{4,})|(\w+) 
cadena:   Kim Jobs Xu Cloyd Mohr Ngo Rock .
coincidencias: ^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^ 
grupo:    222 1111 22 11111 1111 222 1111    
( Ejemplo )

Ayudemos al cerebro a aprender esto y resolver los siguientes 2 problemas:

Utilice llaves {}para reescribir la expresión regular de búsqueda del número de seguro social del paso 7:
patrón:
cadena: 113-25=1902 182-82-0192 H23-_3-9982 1I1-O0-E38B
coincidencias:              ^^^^^^^^^^^
( Solución ) Supongamos que el verificador de seguridad de la contraseña de un sitio web requiere que las contraseñas de los usuarios tengan entre 6 y 12 caracteres. Escriba una expresión regular que marque las contraseñas no válidas en la lista siguiente. Cada contraseña está contenida entre paréntesis ()para facilitar la coincidencia, así que asegúrese de que la expresión regular comience y termine con caracteres literales (y )simbólicos. Sugerencia: asegúrese de no permitir paréntesis literales en contraseñas con [^()]o similares; de lo contrario, terminará haciendo coincidir toda la cadena.
patrón:
cadena:   (12345) (mi contraseña) (Xanadu.2112) (su_do) (¡DeVendedores!)
coincidencias: ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^  
( Solución )

Paso 14: \bSímbolo de borde de ancho cero

20 breves pasos para dominar las expresiones regulares.  Parte 3 - 5La última tarea fue bastante difícil. Pero ¿qué pasaría si lo complicáramos un poco más al incluir las contraseñas entre comillas ""en lugar de paréntesis ()? ¿Podemos escribir una solución similar simplemente reemplazando todos los caracteres entre paréntesis por comillas?
patrón: \"[^"]{0.5}\"|\"[^"]+\s[^"]*\" 
cadena:   "12345" "mi contraseña" " Xanadu.2112 " " su_do" " ¡DeVendedores! "
coincidencias: ^^^^^^^ ^^^^^^^^^^^^^ ^^^ ^^^  
( Ejemplo ) No resultó muy impresionante. ¿Ya has adivinado por qué? El problema es que aquí buscamos contraseñas incorrectas. "Xanadu.2112" es una buena contraseña, por lo que cuando la expresión regular se da cuenta de que esta secuencia no contiene espacios ni caracteres literales ", cede justo antes del carácter "que califica la contraseña en el lado derecho. (Porque especificamos que los caracteres "no se pueden encontrar dentro de las contraseñas usando [^"]). Una vez que el motor de expresiones regulares está satisfecho de que esos caracteres no coinciden con una expresión regular en particular, se ejecuta nuevamente, exactamente donde lo dejó: donde estaba el carácter ", lo que limita " Xanadú.2112" a la derecha. Desde allí ve un carácter de espacio y otro carácter ": ¡para él esta es la contraseña incorrecta! Básicamente, encuentra esta secuencia " "y sigue adelante. Esto no es en absoluto lo que nos gustaría obtener... Sería fantástico si pudiéramos especificar que el primer carácter de la contraseña no debe ser un espacio. ¿Hay alguna forma de hacer esto? (A estas alturas, probablemente ya te habrás dado cuenta de que la respuesta a todas mis preguntas retóricas es "sí"). ¡Sí! ¡Existe tal manera! Muchos motores de expresiones regulares proporcionan una secuencia de escape como "límite de palabra" \b. "Límite de palabra" \bes una secuencia de escape de ancho cero que, curiosamente, coincide con el límite de una palabra. Recuerde que cuando decimos "palabra", nos referimos a cualquier secuencia de caracteres de la clase \wo [A-Za-z0-9_]. Una coincidencia de límites de palabras significa que el carácter inmediatamente anterior o inmediatamente posterior a la secuencia \bdebe ser неun carácter de palabra. Sin embargo, al hacer coincidencias, no incluimos este carácter en nuestra subcadena capturada. Este es un ancho cero. Para ver cómo funciona esto, veamos un pequeño ejemplo:
patrón: \b[^ ]+\b 
cadena:   Todavía quiero dinero , Lebowski .
coincidencias: ^^ ^^^^^ ^^^^ ^^ ^^^^^ ^^^^^^^^  
( Ejemplo ) La secuencia [^ ]debe coincidir con cualquier carácter que no sea un carácter de espacio literal. Entonces, ¿por qué esto no coincide con la coma ,después del dinero o el punto " .después de Lebowski? Esto se debe a que la coma ,y el punto .no son caracteres de palabras, por lo que se crean límites entre los caracteres de palabras y los caracteres que no son palabras. Aparecen entre yel final del palabra dinero y la coma ,que le sigue y entre " ila palabra Lebowski y el punto .(punto/punto) que le sigue. La expresión regular coincide con los límites de estas palabras (pero no con los caracteres que no son palabras y que solo ayudan a definirlas). Pero ¿qué pasa si no incluimos coherencia \ben nuestra plantilla?
patrón: [^ ]+ 
cadena:   Todavía vant ze dinero, Lebowski. 
coincidencias: ^^ ^^^^^ ^^^^ ^^ ^^^^^^ ^^^^^^^^^  
( Ejemplo ) Sí, ahora también encontramos estos signos de puntuación. Ahora usemos límites de palabras para corregir la expresión regular de las contraseñas entre comillas:
patrón: \"\b[^"]{0.5}\b\"|\"\b[^"]+\s[^"]*\b\" 
cadena:   "12345" "mi contraseña" " Xanadu. 2112" "su_do" "DeVendedores!"
coincidencias: ^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^  
( Ejemplo ) Al colocar los límites de las palabras entre comillas ("\b ... \b"), en realidad estamos diciendo que el primer y el último carácter de las contraseñas coincidentes deben ser "caracteres de palabra". Así que esto funciona bien aquí, pero no funcionará tan bien si el primer o último carácter de la contraseña del usuario no es un carácter de palabra:
patrón: \"\b[^"]{0.5}\b\"|\"\b[^"]+\s[^"]*\b\"
cadena: "la siguiente contraseña es demasiado corta" "C++"
partidos:   
( Ejemplo ) Vea cómo la segunda contraseña no está marcada como "no válida" aunque claramente es demasiado corta. Usted debe sercuidadosocon secuencias \b, ya que sólo coinciden con límites entre personajes \wy no \w. En el ejemplo anterior, dado que permitimos caracteres no, en contraseñas \w, no se garantiza que el límite entre \el primer/último carácter de la contraseña sea un límite de palabra \b.

Para completar este paso, resolveremos solo un problema simple:

Los límites de palabras son útiles en los motores de resaltado de sintaxis cuando queremos hacer coincidir una secuencia específica de caracteres, pero queremos asegurarnos de que solo aparezcan al principio o al final de una palabra (o por sí solos). Digamos que estamos escribiendo resaltado de sintaxis y queremos resaltar la palabra var, pero solo cuando aparece sola (sin tocar otros caracteres de la palabra). ¿Puedes escribir una expresión regular para esto? Por supuesto que puedes, es una tarea muy sencilla ;)
patrón:
cadena:   var varx _var ( var j) barvarcar * var var -> { var }
coincidencias: ^^^ ^^^ ^^^ ^^^ ^^^  
( Solución )

Paso 15: "carácter" ^como "principio de línea" y signo de dólar $como "fin de línea"

20 breves pasos para dominar las expresiones regulares.  Parte 3 - 6La secuencia de límites de palabras \b(del último paso de la parte anterior del artículo) no es la única secuencia especial de ancho cero disponible para usar en expresiones regulares. Los dos más populares son "caret" ^- "principio de línea" y signo de dólar $- "fin de línea". Incluir uno de estos en sus expresiones regulares significa que la coincidencia debe aparecer al principio o al final de la cadena fuente:
patrón: ^inicio|fin$ 
cadena:   inicio fin inicio fin inicio fin inicio fin 
coincidencias: ^^^^^ ^^^  
( Ejemplo ) Si su cadena contiene saltos de línea, ^startcoincidirá con la secuencia "inicio" al principio de cualquier línea y end$coincidirá con la secuencia "fin" al final de cualquier línea (aunque esto es difícil de mostrar aquí). Estos símbolos son especialmente útiles cuando se trabaja con datos que contienen delimitadores. Volvamos al tema del "tamaño de archivo" del paso 9 usando ^"inicio de línea". En este ejemplo, los tamaños de nuestros archivos están separados por espacios " ". Por eso queremos que cada tamaño de archivo comience con un número, precedido por un carácter de espacio o el comienzo de una línea:
patrón: (^| )(\d+|\d+\.\d+)[KMGT] 
Cadena B:   6.6KB 1..3KB 12KB 5G 3.3MB KB .6.2TB 9MB .
coincidencias: ^^^^^ ^^^^^ ^^^^^^ ^^^^ 
grupo:    222 122 1222 12    
( Ejemplo ) ¡Ya estamos muy cerca de la meta! Pero puede notar que todavía tenemos un pequeño problema: estamos haciendo coincidir el carácter de espacio antes del tamaño de archivo válido. Ahora podemos simplemente ignorar este grupo de captura (1) cuando nuestro motor de expresiones regulares lo encuentre, o podemos usar un grupo que no captura, que veremos en el siguiente paso.

Mientras tanto, resolvamos 2 problemas más de tono:

Continuando con nuestro ejemplo de resaltado de sintaxis del último paso, algo de resaltado de sintaxis marcará los espacios finales, es decir, cualquier espacio que se encuentre entre un carácter que no sea un espacio en blanco y el final de la línea. ¿Puedes escribir una expresión regular para resaltar solo los espacios finales?
patrón:
cadena: myvec <- c(1, 2, 3, 4, 5)  
coincidencias:                          ^^^^^^^  
( Solución ) Un analizador simple de valores separados por comas (CSV) buscará "tokens" separados por comas. Generalmente, el espacio no tiene significado a menos que esté entre comillas "". Escriba una expresión regular de análisis CSV simple que haga coincidir tokens entre comas, pero ignore (no capture) los espacios en blanco que no estén entre comillas.
patrón:
cadena:   a, "b", "c d",e,f, "g h", dfgi,, k, "", l 
coincide con: ^^ ^^^^ ^^^^^^^^^^ ^^^ ^^^ ^^^^^^ ^^ ^^^ ^ 
grupo:    21 2221 2222212121 222221 222211 21 221 2    
( Solución ) RegEx: 20 pasos cortos para dominar las expresiones regulares. Parte 4.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION