Las sugerencias de este artículo se centran principalmente en la legibilidad de las expresiones regulares. Al desarrollar estos hábitos durante el desarrollo, considerará más claramente el diseño y la estructura de las expresiones, lo que ayudará a reducir los errores y el mantenimiento del código. usted mismo es el mantenedor de este código. Puede echar un vistazo usted mismo y prestar atención a estas experiencias con expresiones regulares en su uso real.
Las expresiones regulares son difíciles de escribir, de leer y de mantener. A menudo no coinciden con texto inesperado o pierden texto válido. Estos problemas se deben al rendimiento y las capacidades de las expresiones regulares. La combinación de capacidades y matices de cada metacarácter hace que el código sea imposible de interpretar sin recurrir a trucos intelectuales.
Muchas herramientas incluyen características que facilitan la lectura y escritura de expresiones regulares, pero también son muy poco idiomáticas. Para muchos programadores, escribir expresiones regulares es un arte mágico. Se apegan a las características que conocen y tienen una actitud de absoluto optimismo. Si está dispuesto a adoptar los cinco hábitos que se analizan en este artículo, podrá diseñar expresiones regulares que resistan prueba y error.
Este artículo utilizará los lenguajes Perl, PHP y Python como ejemplos de código, pero los consejos de este artículo se aplican a casi cualquier implementación de expresión de reemplazo (regex).
1. Utilice espacios y comentarios.
Para la mayoría de los programadores, utilizar espacios y sangrías en un entorno de expresión regular no es un problema. Si no hacen esto, sus compañeros e incluso los profanos se reirán de ellos. Casi todo el mundo sabe que comprimir el código en una línea dificulta su lectura, escritura y mantenimiento. ¿Cuál es la diferencia para las expresiones regulares?
La mayoría de las herramientas de expresión de reemplazo tienen una función de espacios en blanco extendido, que permite a los programadores extender sus expresiones regulares en varias líneas y agregar comentarios al final de cada línea. ¿Por qué sólo un pequeño número de programadores aprovechan esta característica? Las expresiones regulares de Perl 6 utilizan patrones de espacio extendido de forma predeterminada. No dejes que el idioma te amplíe los espacios por defecto, aprovéchalos tú mismo.
Un truco para recordar sobre los espacios en blanco extendidos es decirle al motor de expresiones regulares que ignore los espacios en blanco extendidos. De esta manera, si necesita hacer coincidir espacios, deberá especificarlo explícitamente.
En el lenguaje Perl, agregue x al final de la expresión regular, de modo que "m/foo bar/" adopte la siguiente forma:
m/
foo
bar
/x
En lenguaje PHP, agregue x al final de la expresión regular, de modo que ""/foo bar/"" se convierta en la siguiente forma:
"/
foo
bar
/x"
En el lenguaje Python, pase el parámetro de modificación del patrón "re.VERBOSE" para obtener la función compilada de la siguiente manera:
patrón = r''
foo
bar
'''
regex = re.compile(pattern, re.VERBOSE)
maneja expresiones regulares más complejas, los espacios y comentarios se volverán más importantes. Supongamos que se utiliza la siguiente expresión regular para buscar números de teléfono en los Estados Unidos:
(?d{3})?d{3}[-.]d{4}
Esta expresión regular busca números de teléfono como, por ejemplo, "(314)555-4000", ¿crees que esta expresión regular coincide con "314-555-4000" o "555-4000"? La respuesta es que ninguno coincide. Escribir una línea de código de este tipo oculta las deficiencias y los resultados del diseño en sí. Se requiere el código de área del teléfono, pero la expresión regular carece de un símbolo separador entre el código de área y el prefijo.
Dividir esta línea de código en varias líneas y agregar comentarios expondrá las deficiencias y facilitará su modificación.
En lenguaje Perl debería tener la siguiente forma:
/
(? # paréntesis opcionales
d{3} # Código de área telefónico requerido
)? # paréntesis opcionales
[-s.]? # El delimitador puede ser un guión, un espacio o un punto
d{3} # Prefijo de tres dígitos
[-.] # Otro delimitador
d{4} # Número de teléfono de cuatro dígitos
/x
La expresión regular reescrita ahora tiene un separador opcional después del código de área, por lo que debe coincidir con "314-555-4000", sin embargo, el código de área aún es necesario. Otro programador que necesita hacer que el código de área del teléfono sea opcional puede ver rápidamente que ya no es opcional y un pequeño cambio puede resolver el problema.
2.
Hay tres niveles de prueba en las pruebas de escritura. Cada nivel agrega una capa de confiabilidad a su código. Primero, debe pensar detenidamente qué códigos necesita hacer coincidir y si puede manejar las discrepancias. En segundo lugar, debe utilizar instancias de datos para probar la expresión regular. Finalmente, debes pasar formalmente un panel de prueba.
Decidir qué igualar consiste en realidad en encontrar un equilibrio entre igualar los resultados incorrectos y perder los resultados correctos. Si su expresión regular es demasiado estricta, perderá algunas coincidencias correctas; si es demasiado laxa, producirá una coincidencia incorrecta. Una vez que una expresión regular se incorpora al código real, es posible que no notes ambas. Considere el ejemplo de número de teléfono anterior, que coincidiría con "800-555-4000 = -5355". En realidad, las coincidencias incorrectas son difíciles de detectar, por lo que es importante planificar con anticipación y probarlas bien.
Continuando con el ejemplo del número de teléfono, si está confirmando un número de teléfono en un formulario web, puede estar satisfecho con un número de diez dígitos en cualquier formato. Sin embargo, si desea separar los números de teléfono de una gran cantidad de texto, es posible que deba excluir cuidadosamente las coincidencias falsas que no cumplan con los requisitos.
Cuando piense en los datos que desea comparar, escriba algunos escenarios de casos. Escriba algún código para probar su expresión regular frente a un escenario de caso. Para cualquier expresión regular compleja, es mejor escribir un pequeño programa para probarla, que puede adoptar la siguiente forma específica.
En lenguaje Perl:
#!/usr/bin/perl
my @tests = ( "314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345"
);
para cada mi $prueba (@pruebas) {
si ( $prueba =~ m/
(? # paréntesis opcionales
d{3} # Código de área telefónico requerido
)? # paréntesis opcionales
[-s.]? # El delimitador puede ser un guión, un espacio o un punto
d{3} # Prefijo de tres dígitos
[-s.] # Otro delimitador
d{4} # Número de teléfono de cuatro dígitos
/x) {
print "Coincidente en $testn";
}
demás {
print "Coincidencia fallida en $testn";
}
}
En lenguaje PHP:
<?php
$pruebas = matriz( "314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345" );
$expresión regular = "/
(? # paréntesis opcionales
d{3} # Código de área telefónico requerido
)? # paréntesis opcionales
[-s.]? # El delimitador puede ser un guión, un espacio o un punto
d{3} # Prefijo de tres dígitos
[-s.] # Otro delimitador
d{4} # Número de teléfono de cuatro dígitos
/x";
foreach ($pruebas como $prueba) {
si (preg_match($regex, $prueba)) {
echo "Coincidente en $test
;";
}
demás {
echo "Coincidencia fallida en $test
;";
}
}
?>;
En lenguaje Python:
importar re
pruebas = ["314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345"
]
patrón = r''
(? # paréntesis opcionales
d{3} # Código de área telefónico requerido
)? # paréntesis opcionales
[-s.]? # El delimitador puede ser un guión, un espacio o un punto
d{3} # Prefijo de tres dígitos
[-s.] # Otro delimitador
d{4} # Número de teléfono de cuatro dígitos
'''
expresión regular = re.compile( patrón, re.VERBOSE ) para prueba en pruebas:
si regex.match(prueba):
imprimir "Coincidente en", prueba, "n"
demás:
print "Error en la coincidencia", prueba, "n"
La ejecución del código de prueba revelará otro problema: coincide con "1234-123-12345".
En teoría, es necesario integrar todas las pruebas de toda la aplicación en un equipo de pruebas. Incluso si aún no tiene un grupo de pruebas, sus pruebas de expresiones regulares serán una buena base para ello y ahora es un buen momento para comenzar uno. Incluso si aún no es el momento adecuado para crearla, aún debes ejecutar y probar la expresión regular después de cada modificación. Pasar un poco de tiempo aquí te ahorrará muchos problemas.
3. Grupo de operaciones alternas
El símbolo de operación alterna ( ) tiene una prioridad baja, lo que significa que a menudo alterna más de lo previsto por el programador. Por ejemplo, la expresión regular para extraer direcciones de correo electrónico de texto podría ser la siguiente:
^CC: Para:(.*)
El intento anterior es incorrecto, pero este error a menudo pasa desapercibido. El propósito del código anterior es encontrar el texto que comienza con "CC:" o "Para:" y luego extraer la dirección de correo electrónico al final de esta línea.
Desafortunadamente, si "Para:" aparece en medio de una línea, esta expresión regular no capturará ninguna línea que comience con "CC:" y en su lugar extraerá varios fragmentos de texto aleatorios. Hablando francamente, la expresión regular coincide con una línea que comienza con "CC:" pero no captura nada o coincide con cualquier línea que contenga "Para:" pero captura el resto de la línea. Normalmente, esta expresión regular capturaría una gran cantidad de direcciones de correo electrónico, por lo que nadie notaría el error.
Si desea cumplir con la intención real, debe agregar paréntesis para que quede claro. La expresión regular es la siguiente:
(^CC:) (To:(.*))
Si la intención real es capturar texto que comience con ". CC:" o "To:" el resto de la línea, entonces la expresión regular correcta es:
^(CC: To:)(.*)
Este es un error común de coincidencia incompleta que evitará si adquiere el hábito de agrupar para operaciones alternas Este error.
4. Utilice cuantificadores flexibles.
Muchos programadores evitan el uso de cuantificadores flexibles como "*?", "+?" y "??", aunque harán que la expresión sea más fácil de escribir y comprender.
Los cuantificadores relajados coinciden con la menor cantidad de texto posible, lo que ayuda a que la coincidencia exacta sea exitosa. Si escribiera "foo(.*?)bar", el cuantificador dejaría de coincidir la primera vez que encuentre "bar", no la última vez. Esto es importante si desea capturar "###" de "foo###bar+++bar". Un cuantificador estricto capturaría "###bar++ +". ;), esto causará muchos problemas. Si utiliza cuantificadores relajados, puede generar nuevas expresiones regulares dedicando muy poco tiempo a ensamblar tipos de caracteres.
Los cuantificadores relajados son de gran valor cuando se conoce la estructura del contexto en el que se desea capturar el texto.
5. Utilice los delimitadores disponibles.
Los lenguajes Perl y PHP a menudo usan una barra izquierda (/) para marcar el principio y el final de una expresión regular. El lenguaje Python usa un conjunto de comillas para marcar el principio y el final. Si insiste en utilizar barras izquierdas en Perl y PHP, deberá evitar las barras diagonales en las expresiones; si utiliza comillas en Python, deberá evitar las barras invertidas (). Elegir diferentes delimitadores o comillas puede permitirle evitar la mitad de la expresión regular. Esto hará que las expresiones sean más fáciles de leer y reducirá los posibles errores causados por olvidarse de evitar los símbolos.
Los lenguajes Perl y PHP permiten utilizar como delimitadores cualquier carácter no numérico y de espacio. Si cambia a un nuevo delimitador, puede evitar perder la barra izquierda al hacer coincidir URL o etiquetas HTML (como "http://" o "<br/>;").
Por ejemplo, "/http://(S)*/" se puede escribir como "#http://(S)*#".
Los delimitadores comunes son "#", "!" y " ". Si utiliza corchetes, corchetes angulares o llaves, manténgalos coincidentes. A continuación se muestran algunos ejemplos de delimitadores comunes:
#…# !…! {…} s … … (Solo Perl) s[…][…] (Solo Perl) s<…>;/…/ (Solo Perl)
En Python, una expresión regular se trata primero como una cadena. Si utiliza comillas como delimitadores, se perderán todas las barras invertidas. Pero puedes evitar este problema usando la cadena "r''". Si utiliza tres comillas simples consecutivas para la opción "re.VERBOSE", le permitirá incluir nuevas líneas. Por ejemplo, regex = "( file://w+)(//d +)" se puede escribir de la siguiente forma:
regex = r''
(w+)
(d+)
'''