URL original: http://www.jb51.net/tools/zhengze.html
Objetivo de este artículo
En 30 minutos, comprenderás qué son las expresiones regulares y adquirirás conocimientos básicos para utilizarlas en tus programas o páginas web.
Cómo utilizar este tutorial
Lo más importante: ¡dedícale 30 minutos! Si no tienes experiencia con expresiones regulares, no intentas aprenderlas en 30 segundos, a menos que seas un superhéroe :)
No te asustes con las expresiones complejas que aparecen a continuación. Sigue mis pasos y descubrirás que las expresiones regulares no son tan difíciles como parecen. Por supuesto, es normal que después de leer este tutorial, sientas que entendiste mucho pero casi no recuerdas nada. Considero que es imposible que alguien nuevo en expresiones regulares recuerde más del 80% de la sintaxis después de leer este tutorial. El objetivo es que entiendas los principios fundamentales, pero necesitarás practicar y usarlas frecuentemente para dominarlas.
Además de ser un tutorial introductorio, este artículo también pretende servir como referencia de sintaxis para el trabajo diario. Según mi experiencia, este objetivo se ha cumplido bien: como ves, yo tampoco puedo recordar todo, ¿verdad?
Convenciones de formato: Términos técnicos - Metacaracteres/Sintaxis - Expresión regular - Parte de la expresión regular (para análisis) - Cadena de origen que se va a emparejar - Explicación de la expresión regular o parte de ella.
Notas al margen: A la derecha del artículo hay algunas notas que proporcionan información adicional o explican conceptos básicos para lectores sin programación, generalmente se pueden ignorar.
¿Qué son realmente las expresiones regulares?
Los caracteres son la unidad básica con la que el software procesa texto. Pueden ser letras, números, signos de puntuación, espacios, saltos de línea, caracteres chinos, etc. Una cadena es una secuencia de cero o más caracteres. El texto es básicamente cadenas de caracteres. Cuando decimos que una cadena coincide con una expresión regular, nos referimos a que una parte (o varias partes) de esa cadena cumple con las condiciones especificadas en la expresión.
Al escribir programas o páginas web que procesan cadenas, a menudo surge la necesidad de buscar cadenas que cumplan con reglas complejas. Las expresiones regulares son la herramienta utilizada para describir estas reglas. En otras palabras, las expresiones regulares son código que registra patrones de texto.
Es probable que hayas utilizado los comodines (wildcards) en Windows/DOS para buscar archivos, que son * y ?. Si buscas todos los documentos Word en un directorio, buscarías *.doc. Aquí, * se interpreta como cualquier cadena de caracteres. Al igual que los comodines, las expresiones regulares también son herramientas para emparejar texto, pero a diferencia de los comodines, pueden describir tus necesidades con mayor precisión, aunque a costa de mayor complejidad. Por ejemplo, puedes escribir una expresión regular para buscar cadenas que comiencen con 0, seguidas de 2-3 dígitos, luego un guión "-" y finalmente 7 u 8 dígitos (como 010-12345678 o 0376-7654321).
Introducción
La mejor manera de aprender expresiones regulares es comenzar con ejemplos. Entender los ejemplos y luego modificarlos y experimentar por ti mismo es el enfoque recomendado. A continuación, se presentan varios ejemplos simples con explicaciones detalladas.
Supongamos que buscas "hi" en una novela en inglés. Puedes usar la expresión regular hi.
Esta es casi la expresión regular más simple. Puede coincidir exactamente con cadenas de dos caracteres donde el primero es 'h' y el segundo 'i'. Normalmente, las herramientas que procesan expresiones regulares ofrecen una opción para ignorar mayúsculas y minúsculas. Si se activa esta opción, puede coincidir con hi, HI, Hi, hI.
Desafortunadamente, muchas palabras contienen "hi" como caracteres consecutivos, como him, history, high, etc. Si usas hi para buscar, también encontrarás estas ocurrencias. Si quieres buscar específicamente la palabra "hi", deberías usar \bhi\b.
\b es un código especial (o metacarácter) definido en las expresiones regulares que representa el inicio o el final de una palabra, es decir, los límites de la palabra. Aunque las palabras en inglés generalmente están separadas por espacios, signos de puntuación o saltos de línea, \b no coincide con ninguno de estos caracteres de separación, solo coincide con una posición.
Para ser más precisos, \b coincide con una posición donde el carácter anterior y el siguiente no son ambos \w (uno es y el otro no, o uno no existe).
Si buscas "hi" seguido más adelante por "Lucy", deberías usar \bhi\b.*\bLucy\b.
Aquí, . es otro metacarácter que coincide con cualquier carácter excepto el salto de línea. * también es un metacarácter, pero no representa un carácter ni una posición, sino una cantidad: especifica que el contenido anterior puede repetirse cualquier número de veces para que toda la expresión coincida. Por lo tanto, .* juntos significan cualquier número de caracteres que no sean saltos de línea. Ahora el significado de \bhi\b.*\bLucy\b es claro: primero la palabra "hi", luego cualquier número de caracteres cualesquiera (pero no saltos de línea), y finalmente la palabra "Lucy".
El salto de línea es '\n', un carácter con código ASCII 10 (hexadecimal 0x0A).
Si usamos otros metacaracteres, podemos construir expresiones regulares más potentes. Por ejemplo:
0\d\d-\d\d\d\d\d\d\d\d coincide con cadenas que comienzan con 0, seguidos de dos dígitos, luego un guión "-" y finalmente 8 dígitos (es decir, números de teléfono chinos. Este ejemplo solo coincide con códigos de área de 3 dígitos).
Aquí, \d es un nuevo metacarácter que coincide con un dígito (0, o 1, o 2, etc.). - no es un metacarácter, solo coincide consigo mismo: el guión.
Para evitar tanta repetición molesta, también podemos escribir esta expresión así: 0\d{2}-\d{8}. Aquí, {2} ({8}) después de \d significa que \d debe repetirse exactamente 2 veces (8 veces).
Probando expresiones regulares
Otras herramientas de prueba disponibles:
- RegexBuddy
- Herramienta online de prueba para expresiones regulares de Javascript
Si no encuentras las expresiones regulares difíciles de leer y escribir, o eres un genio o no eres de este planeta. La sintaxis de las expresiones regulares es bastante complicada, incluso para quienes las usan con frecuencia. Debido a que son difíciles de leer y escribir, y propensas a errores, es necesario contar con una herramienta para probarlas.
Los detalles de las expresiones regulares pueden variar en diferentes entornos. Este tutorial describe el comportamiento de las expresiones regulares en Microsoft .Net Framework 4.0, por lo que te recomiendo el herramienta de prueba de expresiones regulares que he desarrollado para .Net. Consulta las instrucciones en esa página para instalar y ejecutar el software.
Aquí tienes una captura de pantalla de Regex Tester en ejecución:
Metacaracteres
Ya conoces varios metacaracteres útiles como \b, ., *, y \d. Las expresiones regulares tienen muchos más metacaracteres. Por ejemplo, \s coincide con cualquier carácter en blanco, incluyendo espacios, tabulaciones, saltos de línea, espacios en chino completo, etc. \w coincide con letras, números, guiones bajos o caracteres chinos.
El manejo especial de chino/caracteres chinos es compatible con el motor de expresiones regulares proporcionado por .Net. Para otros entornos, consulta la documentación relevante.
Veamos más ejemplos:
\ba\w*\b coincide con palabras que comienzan con la letra 'a': primero un inicio de palabra (\b), luego la letra 'a', luego cualquier cantidad de letras o números (\w*), y finalmente un final de palabra (\b).
Ahora expliquemos qué significa una palabra en las expresiones regulares: es una o más \w consecutivas. Sí, tiene poco que ver con las miles de palabras del inglés que tuviste que memorizar al aprender ese idioma.
\d+ coincide con uno o más dígitos consecutivos. Aquí, + es un metacarácter similar a *, pero * coincide con cero o más repeticiones, mientras que + coincide con una o más repeticiones.
\b\w{6}\b coincide con palabras que tienen exactamente 6 caracteres.
Tabla 1. Metacaracteres comunes
| Código | Descripción |
|---|---|
| . | Coincide con cualquier carácter excepto el salto de línea |
| \w | Coincide con letras, números, guion bajo o caracteres chinos |
| \s | Coincide con cualquier carácter en blanco |
| \d | Coincide con dígitos |
| \b | Coincide con el inicio o final de una palabra |
| ^ | Coincide con el inicio de la cadena |
| $ | Coincide con el final de la cadena |
El motor de expresiones regulares generalmente proporciona un método "probar si una cadena coincide con una expresión regular", como el método RegExp.test() en JavaScript o Regex.IsMatch() en .NET. Aquí, coincidir significa que la cadena tiene una parte que cumple con las reglas de la expresión. Si no se usan ^ y $, para \d{5,12}, este método solo garantiza que la cadena contiene de 5 a 12 dígitos consecutivos, no toda la cadena es de 5 a 12 dígitos.
Los metacaracteres ^ (el símbolo en la misma tecla que 6) y $ ambos coinciden con una posición, similar a \b. ^ coincide con el inicio de la cadena que estás buscando, $ coincide con el final. Estos códigos son muy útiles para validar entradas, por ejemplo, si un sitio web requiere que el número QQ sea de 5 a 12 dígitos, puedes usar: ^\d{5,12}$.
Aquí, {5,12} es similar a {2} que mencionamos antes, pero {2} coincide exactamente con 2 repeticiones, mientras que {5,12} coincide con 5 a 12 repeticiones; de lo contrario, no coincide.
Debido al uso de ^ y $, toda la cadena de entrada debe coincidir con \d{5,12}, lo que significa que toda la entrada debe ser de 5 a 12 dígitos. Por lo tanto, si el número QQ coincide con esta expresión regular, cumple con los requisitos.
Similar a la opción de ignorar mayúsculas y minúsculas, algunas herramientas de procesamiento de expresiones regulares tienen una opción de procesamiento multilínea. Si se activa esta opción, ^ y $ significan coincidir con el inicio y final de cada línea.
Escape de caracteres
Si quieres buscar los metacaracteres mismos, como . o *, surge un problema: no puedes especificarlos porque serán interpretados con otro significado. En este caso, debes usar \ para cancelar el significado especial de estos caracteres. Por lo tanto, debes usar . y *. Por supuesto, para buscar \ mismo, también debes usar \.
Por ejemplo: deerchao.net coincide con deerchao.net, C:\Windows coincide con C:\Windows.
Repeticiones
Hemos visto los métodos de repetición *, +, {2}, {5,12} anteriores. A continuación se muestran todos los cuantificadores en expresiones regulares (códigos que especifican cantidades, como *, {5,12}, etc.):
Tabla 2. Cuantificadores comunes
| Código/Sintaxis | Descripción |
|---|---|
| * | Repite cero o más veces |
| + | Repite una o más veces |
| ? | Repite cero o una vez |
| {n} | Repite exactamente n veces |
| {n,} | Repite n o más veces |
| {n,m} | Repite de n a m veces |
Aquí hay algunos ejemplos de uso de repeticiones:
Windows\d+ coincide con "Windows" seguido de uno o más dígitos
^\w+ coincide con la primera palabra de una línea (o la primera palabra de toda la cadena, dependiendo de la configuración de opciones)
Clases de caracteres
Buscar dígitos, letras, dígitos o espacios en blanco es simple porque ya existen metacaracteres para estos conjuntos de caracteres. Pero, ¿qué pasa si quieres coincidir con un conjunto de caracteres que no tiene metacaracteres predefinidos (como las vocales a, e, i, o, u)?
Es simple, solo necesitas listarlos entre corchetes, como [aeiou] coincide con cualquier vocal en inglés, [.?!] coincide con signos de puntuación (. o ? o !).
También podemos especificar fácilmente un rango de caracteres, como [0-9] significa exactamente lo mismo que \d: un dígito. De manera similar, [a-z0-9A-Z_] es completamente equivalente a \w (si solo consideramos inglés).
Aquí hay una expresión más compleja: (?0\d{2})?[- ]?\d{8}.
Los paréntesis "(" y ")" también son metacaracteres, se mencionarán en la sección de agrupación, por lo que aquí necesitamos usar escape.
Esta expresión puede coincidir con varios formatos de números de teléfono, como (010)88886666, 022-22334455, o 02912345678. Analicémosla: primero un carácter de escape (, puede aparecer cero o una vez (?), luego un 0 seguido de dos dígitos (\d{2}), luego ) o - o espacio, aparece una vez o no (?), finalmente ocho dígitos (\d{8}).
Condiciones de ramificación
Desafortunadamente, la expresión anterior también puede coincidir con formatos "incorrectos" como 010)12345678 o (022-87654321. Para resolver este problema, necesitamos usar condiciones de ramificación. Las condiciones de ramificación en expresiones regulares se refieren a tener varias reglas, si se cumple cualquiera de ellas, se considera una coincidencia. El método específico es usar | para separar diferentes reglas. ¿No lo entiendes? No te preocupes, mira el ejemplo:
0\d{2}-\d{8}|0\d{3}-\d{7} esta expresión puede coincidir con dos tipos de números de teléfono separados por guiones: uno con código de área de 3 dígitos y número local de 8 dígitos (como 010-12345678), y otro con código de área de 4 dígitos y número local de 7 dígitos (0376-2233445).
(?0\d{2})?[- ]?\d{8}|0\d{2}[- ]?\d{8} esta expresión coincide con números de teléfono con código de área de 3 dígitos, donde el código de área puede estar entre paréntesis o no, y puede haber un guión o espacio entre el código de área y el número local, o no haber separación. Puedes intentar usar condiciones de ramificación para expandir esta expresión para que también admita códigos de área de 4 dígitos.
\d{5}-\d{4}|\d{5} esta expresión se usa para códigos postales de EE.UU. Las reglas son 5 dígitos, o 9 dígitos separados por guion. Se da este ejemplo porque ilustra un punto: al usar condiciones de ramificación, prestar atención al orden de las condiciones. Si lo cambias a \d{5}|\d{5}-\d{4}, solo coincidirá con códigos postales de 5 dígitos (y los primeros 5 dígitos de códigos de 9 dígitos). La razón es que al probar condiciones de ramificación, se prueba cada condición de izquierda a derecha, y si se cumple una condición, no se consideran las otras condiciones.
Agrupación
Hemos mencionado cómo repetir un solo carácter (simplemente agregando un cuantificador después del carácter). Pero, ¿qué pasa si quieres repetir múltiples caracteres? Puedes usar paréntesis para especificar subexpresiones (también llamadas grupos), y luego puedes especificar el número de repeticiones de esta subexpresión. También puedes realizar otras operaciones en la subexpresión (se mencionará más adelante).
(\d{1,3}.){3}\d{1,3} es una expresión simple para coincidir con direcciones IP. Para entenderla, analízala en el siguiente orden: \d{1,3} coincide con 1 a 3 dígitos, (\d{1,3}.){3} coincide con tres dígitos más un punto (este grupo en su conjunto) repetido 3 veces, y finalmente uno a tres dígitos (\d{1,3}).
Cada número en una dirección IP no puede ser mayor que 255. A menudo me preguntan si números como 01.02.03.04 con ceros iniciales son direcciones IP válidas. La respuesta es: sí, los números en las direcciones IP pueden contener ceros iniciales (leading zeroes).
Desafortunadamente, también coincidirá con direcciones IP imposibles como 256.300.888.999. Si se pudieran usar comparaciones aritméticas, este problema podría resolverse simplemente, pero las expresiones regulares no proporcionan ninguna función matemática, por lo que solo se pueden usar agrupaciones largas, opciones y clases de caracteres para describir una dirección IP correcta: ((2[0-4]\d|25[0-5]|[01]?\d\d?).){3}(2[0-4]\d|25[0-5]|[01]?\d\d?).
La clave para entender esta expresión es entender 2[0-4]\d|25[0-5]|[01]?\d\d?, no entraré en detalles, deberías poder analizar su significado por ti mismo.
Negación
A veces necesitamos buscar caracteres que no pertenecen a una clase de caracteres que se puede definir fácilmente. Por ejemplo, si queremos buscar cualquier carácter excepto dígitos, necesitamos usar negación:
Tabla 3. Códigos de negación comunes
| Código/Sintaxis | Descripción |
|---|---|
| \W | Coincide con cualquier carácter que no sea letra, número, guion bajo o carácter chino |
| \S | Coincide con cualquier carácter que no sea un espacio en blanco |
| \D | Coincide con cualquier carácter que no sea un dígito |
| \B | Coincide con una posición que no es el inicio o final de una palabra |
| [^x] | Coincide con cualquier carácter excepto x |
| [^aeiou] | Coincide con cualquier carácter excepto las vocales a, e, i, o, u |
Ejemplo: \S+ coincide con cadenas que no contienen espacios en blanco.
]+> coincide con cadenas entre corchetes angulares que comienzan con a.
Referencias hacia atrás
Al usar paréntesis para especificar una subexpresión, el texto que coincide con esta subexpresión (es decir, el contenido capturado por este grupo) puede procesarse más adelante en la expresión u otros programas. De forma predeterminada, cada grupo tiene automáticamente un número de grupo, la regla es: de izquierda a derecha, marcada por el paréntesis izquierdo del grupo, el primer grupo tiene número 1, el segundo 2, y así sucesivamente.
Uh... en realidad, la asignación de números de grupo no es tan simple como acabo de decir:
- El grupo 0 corresponde a toda la expresión regular
- El proceso de asignación de números de grupo en realidad requiere escanear de izquierda a derecha dos veces: la primera pasada solo asigna grupos sin nombre, la segunda pasada solo asigna grupos con nombre -- por lo tanto, todos los números de grupo de los grupos con nombre son mayores que los de los grupos sin nombre
- Puedes usar la sintaxis (?:exp) para quitar a un grupo del derecho a asignar un número de grupo.
Las referencias hacia atrás se usan para buscar repetidamente el texto coincidente con un grupo anterior. Por ejemplo, \1 representa el texto coincidente con el grupo 1. ¿Difícil de entender? Mira el ejemplo:
\b(\w+)\b\s+\1\b puede usarse para coincidir con palabras repetidas, como go go, o kitty kitty. Esta expresión primero busca una palabra, es decir, entre el inicio y final de una palabra (\b(\w+)\b), esta palabra será capturada en el grupo con número 1, luego uno o varios espacios en blanco (\s+), y finalmente el contenido capturado en el grupo 1 (es decir, la palabra coincidente anterior) (\1).
También puedes especificar tu propio nombre para una subexpresión. Para especificar un nombre para una subexpresión, usa esta sintaxis: (?\w+) (o puedes usar comillas simples en lugar de corchetes: (?'Word'\w+)), así asignas el nombre "Word" a \w+. Para hacer referencia a este contenido capturado, puedes usar \k, por lo que el ejemplo anterior también puede escribirse así: \b(?\w+)\b\s+\k\b.
Al usar paréntesis, hay muchas sintaxis con propósitos específicos. A continuación se enumeran las más comunes:
Tabla 4. Sintaxis de agrupación común
| Categoría | Código/Sintaxis | Descripción |
|---|---|---|
| Captura | (exp) | Coincide con exp y captura el texto en un grupo con nombre automático |
| (?exp) | Coincide con exp y captura el texto en un grupo con nombre "name", también se puede escribir (?'name'exp) | |
| (?:exp) | Coincide con exp, no captura el texto coincidente, y no asigna número de grupo a este grupo | |
| Aserción de ancho cero | (?=exp) | Coincide con la posición antes de exp |
| (?<=exp) | Coincide con la posición después de exp | |
| (?!exp) | Coincide con la posición que no está seguida por exp | |
| (?<!exp) | Coincide con la posición que no está precedida por exp | |
| Comentario | (?#comment) | Este tipo de grupo no afecta el procesamiento de la expresión regular, se usa para proporcionar comentarios para la lectura |
Hemos discutido las dos primeras sintaxis. La tercera (?:exp) no cambia la forma de procesar la expresión regular, pero el contenido coincidente por este grupo no será capturado como en los dos primeros casos, y no tendrá número de grupo. "¿Por qué querría hacer esto?" - Buena pregunta, ¿crees que por qué?
Aserciones de ancho cero
¿Terrestres, no te parecen estos nombres de términos demasiado complejos y difíciles de recordar? Yo también lo siento. Solo necesitas saber que existe algo así, no importa cómo se llame... Si una persona no tiene nombre, puede concentrarse en practicar la espada; si las cosas no tienen nombre, pueden ser tomadas o dejadas a discreción...
Las siguientes cuatro se usan para buscar algo antes o después de ciertos contenidos (pero sin incluir estos contenidos), es decir, al igual que \b, ^, $, se usan para especificar una posición que debe cumplir ciertas condiciones (es decir, una aserción), por lo que también se llaman aserciones de ancho cero. Mejor veamos ejemplos:
Una aserción declara un hecho que debería ser verdadero. En las expresiones regulares, solo cuando la aserción es verdadera continúa la coincidencia.
(?=exp) también se llama aserción de ancho cero positiva de anticipación, afirma que la posición donde aparece puede coincidir con la expresión exp. Por ejemplo, \b\w+(?=\ing\b), coincide con la parte de una palabra que termina en ing (excepto ing), al buscar "I'm singing while you're dancing", coincidirá con "sing" y "danc".
(?<=exp) también se llama aserción de ancho cero positiva de retrospectiva, afirma que la posición donde aparece puede coincidir con la expresión exp. Por ejemplo, (?<=\bre)\w+\b coincidirá con la segunda parte de palabras que comienzan con re (excepto re), por ejemplo, al buscar "reading a book", coincidirá con "ading".
Si quieres agregar una coma cada tres dígitos en un número largo (naturalmente, desde la derecha), puedes buscar las partes donde necesitas agregar comas antes y dentro: ((?<=\d)\d{3})+\b, al usarlo para buscar en 1234567890, el resultado es 234567890.
El siguiente ejemplo usa ambas aserciones: (?<=\s)\d+(?=\s) coincide con números delimitados por espacios en blanco (nuevamente, sin incluir estos espacios en blanco).
Aserciones negativas de ancho cero
Anteriormente mencionamos cómo buscar caracteres que no son un carácter específico o no están en una clase de caracteres específica (negación). Pero, ¿qué pasa si solo queremos asegurarnos de que un carácter no aparece, pero no queremos coincidir con él? Por ejemplo, si queremos buscar palabras que contengan la letra q, pero q no va seguida de la letra u, podemos intentar esto:
\b\wq[^u]\w\b coincide con palabras que contienen una letra q que no va seguida de la letra u. Pero si pruebas más (o eres lo suficientemente perspicaz para observar directamente), descubrirás que si q aparece al final de la palabra, como en Iraq, Benq, esta expresión fallará. Esto se porque [^u] siempre debe coincidir con un carácter, por lo que si q es el último carácter de la palabra, [^u] coincidirá con el separador de palabras después de q (que podría ser un espacio, o un punto, u otra cosa), y \w*\b coincidirá con la siguiente palabra, por lo que \b\wq[^u]\w\b puede coincidir con toda la cadena "Iraq fighting". Las aserciones negativas de ancho cero pueden resolver este problema porque solo coinciden con una posición y no consumen ningún carácter. Ahora podemos resolver el problema así: \b\wq(?!u)\w\b.
La aserción negativa de ancho cero de anticipación (?!exp), afirma que la posición donde aparece no puede coincidir con la expresión exp. Por ejemplo: \d{3}(?!\d) coincide con tres dígitos, y estos tres dígitos no pueden ir seguidos de otro dígito; \b((?!abc)\w)+\b coincide con palabras que no contienen la cadena continua "abc".
De manera similar, podemos usar (?<!exp), la aserción negativa de ancho cero de retrospectiva, para afirmar que la posición donde aparece no puede coincidir con la expresión exp: (?<![a-z])\d{7} coincide con siete dígitos que no van precedidos de una letra minúscula.
Analicemos detalladamente la expresión (?<=<(\w+)>).*(?=</\1>), esta expresión muestra mejor el uso real de las aserciones de ancho cero.
Un ejemplo más complejo: (?<=<(\w+)>).(?=</\1>) coincide con el contenido dentro de etiquetas HTML simples sin atributos. (?<=<(\w+)>) especifica un prefijo: una palabra entre corchetes angulares (por ejemplo, podría ser ), luego .(cualquier cadena), y finalmente un sufijo (?=</\1>). Ten en cuenta que / en el sufijo usa el escape de caracteres que mencionamos antes; \1 es una referencia hacia atrás, que se refiere al contenido capturado en el primer grupo, el contenido coincidente con (\w+) anterior, por lo que si el prefijo es realmente , el sufijo es . La expresión completa coincide con el contenido entre y (nuevamente, sin incluir el prefijo y el sufijo mismos).
Comentarios
Otro uso de los paréntesis es incluir comentarios mediante la sintaxis (?#comment). Por ejemplo: 2[0-4]\d(?#200-249)|250-5|[01]?\d\d?(?#0-199).
Para incluir comentarios, es mejor activar la opción "ignorar espacios en blanco en el patrón", así al escribir la expresión puedes agregar espacios, tabulaciones, saltos de línea, y al usarlos realmente estos serán ignorados. Al activar esta opción, todo el texto desde # hasta el final de la línea será tratado como comentario y se ignorará. Por ejemplo, podemos escribir la expresión anterior así:
(?<= # Afirmar el prefijo del texto a coincidir
<(\w+)> # Buscar una palabra entre corchetes angulares (es decir, etiquetas HTML/XML)
) # Fin del prefijo
.* # Coincidir con cualquier texto
(?= # Afirmar el sufijo del texto a coincidir
<\/\1> # Buscar contenido entre corchetes angulares: un "/" seguido de la etiqueta capturada antes
) # Fin del sufijo
Codicioso vs. perezoso
Cuando una expresión regular contiene cuantificadores que pueden aceptar repeticiones, el comportamiento típico es (siempre que toda la expresión pueda coincidir) coincidir con tantos caracteres como sea posible. Tomando esta expresión como ejemplo: a.*b, coincidirá con la cadena más larga que comienza con a y termina con b. Si la usamos para buscar en "aabab", coincidirá con toda la cadena "aabab". Esto se llama coincidencia codiciosa.
A veces, necesitamos una coincidencia perezosa, es decir, coincidir con tan pocos caracteres como sea posible. Los cuantificadores que dimos antes pueden convertirse en modo perezoso simplemente agregando un signo de interrogación ? después de ellos. Así, .*? significa repetir cualquier cantidad de veces, pero usar la menor repetición posible para que toda la coincidencia tenga éxito. Ahora veamos el ejemplo perezoso:
a.*?b coincide con la cadena más corta que comienza con a y termina con b. Si la aplicamos a "aabab", coincidirá con "aab" (caracteres 1-3) y "ab" (caracteres 4-5).
¿Por qué la primera coincidencia es "aab" (caracteres 1-3) en lugar de "ab" (caracteres 2-3)? En resumen, porque las expresiones regulares tienen otra regla con prioridad más alta que la regla codiciosa/perezosa: la coincidencia que comienza primero tiene la mayor prioridad —The match that begins earliest wins.
Tabla 5. Cuantificadores perezosos
| Código/Sintaxis | Descripción |
|---|---|
| *? | Repite cualquier número de veces, pero tan pocas como sea posible |
| +? | Repite una o más veces, pero tan pocas como sea posible |
| ?? | Repite cero o una vez, pero tan pocas como sea posible |
| {n,m}? | Repite de n a m veces, pero tan pocas como sea posible |
| {n,}? | Repite n o más veces, pero tan pocas como sea posible |
Opciones de procesamiento
En C#, puedes usar el constructor Regex(String, RegexOptions) para establecer opciones de procesamiento de expresiones regulares. Por ejemplo: Regex regex = new Regex(@"\ba\w{6}\b", RegexOptions.IgnoreCase);
Se han introducido varias opciones como ignorar mayúsculas/minúsculas, modo multilínea, etc. Estas opciones se pueden usar para cambiar la forma de procesar las expresiones regulares. A continuación se muestran las opciones comunes en .NET:
Tabla 6. Opciones de procesamiento comunes
| Nombre | Descripción |
|---|---|
| IgnoreCase(ignorar mayúsculas/minúsculas) | Coincidir sin distinguir mayúsculas y minúsculas. |
| Multiline(modo multilínea) | Cambia el significado de ^ y $, para que coincidan con el inicio y final de cualquier línea, no solo con el inicio y final de toda la cadena. (En este modo, el significado exacto de $ es: coincidir con la posición antes de \n y la posición antes del final de la cadena.) |
| Singleline(mono-línea) | Cambia el significado de ., para que coincida con cada carácter (incluyendo el salto de línea \n). |
| IgnorePatternWhitespace(ignorar espacios) | Ignora los espacios no escapados en la expresión y habilita los marcados por # como comentarios. |
| ExplicitCapture(captura explícita) | Solo captura los grupos que han sido nombrados explícitamente. |
Una pregunta frecuente es: ¿solo se puede usar el modo multilínea o el modo mono-línea a la vez? La respuesta es: no. No hay ninguna relación entre estas dos opciones, excepto que sus nombres son similares (lo que causa confusión).
Grupos balanceados / coincidencia recursiva
La sintaxis de grupos balanceados presentada aquí es compatible con .NET Framework; otros lenguajes/bibliotecas pueden no admitir esta función, o pueden admitirla pero con sintaxis diferente.
A veces necesitamos coincidir con estructuras jerárquicas anidadas como ( 100 * ( 50 + 15 ) ), en este caso, simplemente usar (.+) solo coincidirá con el contenido entre el paréntesis izquierdo más a la izquierda y el paréntesis derecho más a la derecha (aquí discutimos el modo codicioso, el modo perezoso tiene problemas similares). Si el número de paréntesis izquierdo y derecho en la cadena original no es igual, como ( 5 / ( 3 + 2 ) ) ), el número de ambos en nuestro resultado de coincidencia tampoco será igual. ¿Hay alguna manera de capturar el contenido entre paréntesis emparejados más largo en tal cadena?
Para evitar que ( y ( confundan por completo tu cerebro, usemos corchetes angulares en lugar de paréntesis redondos. Ahora nuestro problema se convierte en cómo capturar el contenido entre corchetes angulares emparejados más largo en una cadena como xx aa> yy.
Aquí necesitamos usar las siguientes construcciones de sintaxis:
- (?'group') Captura el contenido y lo nombra "group", y lo empuja a la pila (Stack)
- (?'-group') Saca de la pila el último contenido capturado llamado "group", si la pila estaba vacía, la coincidencia de este grupo falla
- (?(group)yes|no) Si hay contenido capturado llamado "group" en la pila, continúa coincidiendo con la expresión en la parte yes, de lo contrario continúa con la parte no
- (?!) Aserción negativa de ancho cero de anticipación, como no hay expresión posterior, siempre intenta fallar
Si no eres programador (o te llamas programador pero no sabes qué es una pila), entiende estas tres sintaxis así: la primera escribe "group" en un pizarrón, la segunda borra un "group" del pizarrón, y la tercera mira si todavía hay "group" escrito en el pizarrón; si hay, continúa coincidiendo con la parte yes, de lo contrario continúa con la parte no.
Lo que necesitamos hacer es: cada vez que encontramos un paréntesis izquierdo, empujamos un "Open" a la pila, cada vez que encontramos un paréntesis derecho, sacamos uno, y al final vemos si la pila está vacía; si no está vacía, prueba que hay más paréntesis izquierdos que derechos, por lo que la coincidencia debería fallar. El motor de expresiones regulares hará retroceso (abandonando algunos caracteres al principio o al final), tratando de hacer que toda la expresión coincida.
< # Paréntesis angular más externo izquierdo
[^<>]* # Contenido que no son paréntesis después del paréntesis angular más externo izquierdo
(
(
(?'Open'<) # Encontrado un paréntesis angular izquierdo, escribir "Open" en el pizarrón
[^<>]* # Coincide con contenido que no son paréntesis después del paréntesis angular izquierdo
)+
(
(?'-Open'>) # Encontrado un paréntesis angular derecho, borrar un "Open"
[^<>]* # Coincide con contenido que no son paréntesis después del paréntesis angular derecho
)+
)*
(?(Open)(?!)) # Antes del paréntesis angular más externo derecho, verificar si todavía hay "Open" en el pizarrón; si todavía hay, la coincidencia falla
> # Paréntesis angular más externo derecho
Una aplicación común de los grupos balanceados es coincidir con HTML, el siguiente ejemplo puede coincidir con etiquetas anidadas: ]>.(((?'Open']>)[^<>])+((?'-Open')[^<>]))*(?(Open)(?!)).
¿Qué más no se ha mencionado?
Se han dsecrito numerosos elementos para construir expresiones regulares, pero todavía hay muchas cosas no mencionadas. A continuación se muestra una lista de elementos no mencionados, que incluyen sintaxis y explicaciones simples. Puedes encontrar referencias más detalladas en línea para aprenderlos--cuando los necesites. Si tienes instalada la MSDN Library, también puedes encontrar documentación detallada sobre expresiones regulares en .NET. La introducción aquí es muy breve, si necesitas más información y no tienes MSDN Library instalada, puedes consultar la documentación en línea de MSDN sobre elementos del lenguaje de expresiones regulares.
Tabla 7. Sintaxis no discutida en detalle
| Código/Sintaxis | Descripción |
|---|---|
| \a | Carácter de alarma (imprimirlo hace que el电脑 emita un pitido) |
| \b | Generalmente es la posición límite de palabra, pero si se usa en una clase de caracteres representa retroceso |
| \t | Tabulador, Tab |
| \r | Retorno de carro |
| \v | Tabulador vertical |
| \f | Salto de página |
| \n | Salto de línea |
| \e | Escape |
| \0nn | Carácter con código octal nn en ASCII |
| \xnn | Carácter con código hexadecimal nn en ASCII |
| \unnnn | Carácter Unicode con código hexadecimal nnnn |
| \cN | Carácter de control ASCII. Por ejemplo \cC representa Ctrl+C |
| \A | Inicio de cadena (similar a ^, pero no afectado por la opción multilínea) |
| \Z | Final de cadena o final de línea (no afectado por la opción multilínea) |
| \z | Final de cadena (similar a $, pero no afectado por la opción multilínea) |
| \G | Inicio de la búsqueda actual |
| \p{name} | Clase de caracteres Unicode con nombre name, por ejemplo \p{IsGreek} |
| (?>exp) | Subexpresión codiciosa |
| (?-exp) | Grupo balanceado |
| (?im-nsx:exp) | Cambiar opciones de procesamiento en la subexpresión exp |
| (?im-nsx) | Cambiar opciones de procesamiento para la parte posterior de la expresión |
| (?(exp)yes | no) |
| (?(exp)yes) | Lo anterior, pero usar una expresión vacía como no |
| (?(name)yes | no) |
| (?(name)yes) | Lo anterior, pero usar una expresión vacía como no |
Contactar al autor
Bueno, admito que te engañé, seguro que has tardado más de 30 minutos en llegar aquí. Confía en mí, es mi culpa, no porque seas tonto. Dije "30 minutos" para que tuvieras confianza y paciencia para continuar. Como has llegado hasta aquí, prueba que mi conspiración tuvo éxito. ¿No se siente genial haber sido engañado?
Si quieres quejarte o crees que podría haber engañado mejor, bienvenido a mi Weibo para que lo sepa. Si tienes preguntas sobre expresiones regulares, puedes hacer preguntas en el sitio web stackoverflow, recuerda agregar la etiqueta regex. Si prefieres comunicarte en chino, puedes hacer preguntas en Weibo con la etiqueta #正则#.