regex - online - ¿Por qué las expresiones regulares son tan controvertidas?
regex python (22)
Haciendo Regexes Mantenibles
Un gran avance hacia la desmitificación de los patrones a los que anteriormente se hacía referencia como “expresiones regulares” es el indicador de expresión regular /x
Perl, que a veces se escribe (?x)
cuando está incrustado, que permite espacios en blanco (división de líneas, sangría) y comentarios. Esto mejora seriamente la legibilidad y por lo tanto la capacidad de mantenimiento. El espacio en blanco permite la fragmentación cognitiva, para que pueda ver qué grupos con qué.
Los patrones modernos ahora también admiten referencias relativamente numeradas y con nombre ahora. Eso significa que ya no necesita contar los grupos de captura para darse cuenta de que necesita $4
o /7
. Esto ayuda cuando se crean patrones que se pueden incluir en otros patrones.
Aquí hay un ejemplo de un grupo de captura relativamente numerado:
$dupword = qr{ /b (?: ( /w+ ) (?: /s+ /g{-1} )+ ) /b }xi; $quoted = qr{ ( ["''] ) $dupword /1 }x;
Y aquí hay un ejemplo del enfoque superior de capturas nombradas:
$dupword = qr{ /b (?: (?<word> /w+ ) (?: /s+ /k<word> )+ ) /b }xi;
$quoted = qr{ (?<quote> ["''] ) $dupword /g{quote} }x;
Expresiones gramaticales
Lo mejor de todo es que estas capturas con nombre se pueden colocar dentro de un bloque (?(DEFINE)...)
, para que pueda separar la declaración de la ejecución de elementos con nombre individuales de sus patrones. Esto los hace actuar más bien como subrutinas dentro del patrón.
Un buen ejemplo de este tipo de "expresión regular gramatical" se puede encontrar en esta respuesta y esta . Estos se parecen mucho más a una declaración gramatical.
Como este último te recuerda:
… Asegúrese de nunca escribir patrones de ruido de línea. No tienes que hacerlo, y no deberías. No se puede mantener ningún lenguaje de programación que prohíba los espacios en blanco, los comentarios, las subrutinas o los identificadores alfanuméricos. Así que usa todas esas cosas en tus patrones.
Esto no puede ser sobre enfatizado. Por supuesto, si no usas esas cosas en tus patrones, a menudo crearás una pesadilla. Pero si los usas, sin embargo, no necesitas.
Aquí hay otro ejemplo de un patrón gramatical moderno, este para analizar RFC 5322: use 5.10.0;
$rfc5322 = qr{
(?(DEFINE)
(?<address> (?&mailbox) | (?&group))
(?<mailbox> (?&name_addr) | (?&addr_spec))
(?<name_addr> (?&display_name)? (?&angle_addr))
(?<angle_addr> (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
(?<group> (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ; (?&CFWS)?)
(?<display_name> (?&phrase))
(?<mailbox_list> (?&mailbox) (?: , (?&mailbox))*)
(?<addr_spec> (?&local_part) /@ (?&domain))
(?<local_part> (?&dot_atom) | (?"ed_string))
(?<domain> (?&dot_atom) | (?&domain_literal))
(?<domain_literal> (?&CFWS)? /[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
/] (?&CFWS)?)
(?<dcontent> (?&dtext) | (?"ed_pair))
(?<dtext> (?&NO_WS_CTL) | [/x21-/x5a/x5e-/x7e])
(?<atext> (?&ALPHA) | (?&DIGIT) | [!#/$%&''*+-/=?^_`{|}~])
(?<atom> (?&CFWS)? (?&atext)+ (?&CFWS)?)
(?<dot_atom> (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
(?<dot_atom_text> (?&atext)+ (?: /. (?&atext)+)*)
(?<text> [/x01-/x09/x0b/x0c/x0e-/x7f])
(?<quoted_pair> // (?&text))
(?<qtext> (?&NO_WS_CTL) | [/x21/x23-/x5b/x5d-/x7e])
(?<qcontent> (?&qtext) | (?"ed_pair))
(?<quoted_string> (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
(?&FWS)? (?&DQUOTE) (?&CFWS)?)
(?<word> (?&atom) | (?"ed_string))
(?<phrase> (?&word)+)
# Folding white space
(?<FWS> (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
(?<ctext> (?&NO_WS_CTL) | [/x21-/x27/x2a-/x5b/x5d-/x7e])
(?<ccontent> (?&ctext) | (?"ed_pair) | (?&comment))
(?<comment> /( (?: (?&FWS)? (?&ccontent))* (?&FWS)? /) )
(?<CFWS> (?: (?&FWS)? (?&comment))*
(?: (?:(?&FWS)? (?&comment)) | (?&FWS)))
# No whitespace control
(?<NO_WS_CTL> [/x01-/x08/x0b/x0c/x0e-/x1f/x7f])
(?<ALPHA> [A-Za-z])
(?<DIGIT> [0-9])
(?<CRLF> /x0d /x0a)
(?<DQUOTE> ")
(?<WSP> [/x20/x09])
)
(?&address)
}x;
¿No es eso extraordinario, y espléndido? ¡Puedes tomar una gramática estilo BNF y traducirla directamente en código sin perder su estructura fundamental!
Si los patrones gramaticales modernos aún no son suficientes para usted, entonces el brillante módulo Regexp::Grammars
Damian Conway ofrece una sintaxis aún más clara, con una depuración superior, también. Este es el mismo código para analizar la refundición de RFC 5322 en un patrón de ese módulo:
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Data::Dumper "Dumper";
my $rfc5322 = do {
use Regexp::Grammars; # ...the magic is lexically scoped
qr{
# Keep the big stick handy, just in case...
# <debug:on>
# Match this...
<address>
# As defined by these...
<token: address> <mailbox> | <group>
<token: mailbox> <name_addr> | <addr_spec>
<token: name_addr> <display_name>? <angle_addr>
<token: angle_addr> <CFWS>? /< <addr_spec> /> <CFWS>?
<token: group> <display_name> : (?:<mailbox_list> | <CFWS>)? ; <CFWS>?
<token: display_name> <phrase>
<token: mailbox_list> <[mailbox]> ** (,)
<token: addr_spec> <local_part> /@ <domain>
<token: local_part> <dot_atom> | <quoted_string>
<token: domain> <dot_atom> | <domain_literal>
<token: domain_literal> <CFWS>? /[ (?: <FWS>? <[dcontent]>)* <FWS>?
<token: dcontent> <dtext> | <quoted_pair>
<token: dtext> <.NO_WS_CTL> | [/x21-/x5a/x5e-/x7e]
<token: atext> <.ALPHA> | <.DIGIT> | [!#/$%&''*+-/=?^_`{|}~]
<token: atom> <.CFWS>? <.atext>+ <.CFWS>?
<token: dot_atom> <.CFWS>? <.dot_atom_text> <.CFWS>?
<token: dot_atom> <.CFWS>? <.dot_atom_text> <.CFWS>?
<token: dot_atom_text> <.atext>+ (?: /. <.atext>+)*
<token: text> [/x01-/x09/x0b/x0c/x0e-/x7f]
<token: quoted_pair> // <.text>
<token: qtext> <.NO_WS_CTL> | [/x21/x23-/x5b/x5d-/x7e]
<token: qcontent> <.qtext> | <.quoted_pair>
<token: quoted_string> <.CFWS>? <.DQUOTE> (?:<.FWS>? <.qcontent>)*
<.FWS>? <.DQUOTE> <.CFWS>?
<token: word> <.atom> | <.quoted_string>
<token: phrase> <.word>+
# Folding white space
<token: FWS> (?: <.WSP>* <.CRLF>)? <.WSP>+
<token: ctext> <.NO_WS_CTL> | [/x21-/x27/x2a-/x5b/x5d-/x7e]
<token: ccontent> <.ctext> | <.quoted_pair> | <.comment>
<token: comment> /( (?: <.FWS>? <.ccontent>)* <.FWS>? /)
<token: CFWS> (?: <.FWS>? <.comment>)*
(?: (?:<.FWS>? <.comment>) | <.FWS>)
# No whitespace control
<token: NO_WS_CTL> [/x01-/x08/x0b/x0c/x0e-/x1f/x7f]
<token: ALPHA> [A-Za-z]
<token: DIGIT> [0-9]
<token: CRLF> /x0d /x0a
<token: DQUOTE> "
<token: WSP> [/x20/x09]
}x;
};
while (my $input = <>) {
if ($input =~ $rfc5322) {
say Dumper /%/; # ...the parse tree of any successful match
# appears in this punctuation variable
}
}
Hay muchas cosas buenas en la página de manual de perlre , pero estas mejoras dramáticas en las características fundamentales del diseño de expresiones regulares no se limitan a Perl solo. De hecho, la página de manual de pcrepattern puede ser más fácil de leer, y cubre el mismo territorio.
Los patrones modernos no tienen casi nada en común con las cosas primitivas que aprendiste en tu clase de autómatas finitos.
Al explorar expresiones regulares (también conocidas como RegEx-es), hay muchas personas que parecen ver expresiones regulares como el Santo Grial. Algo que parece tan complicado, solo debe ser la respuesta a cualquier pregunta. Tienden a pensar que cada problema es solucionable usando expresiones regulares.
Por otro lado, también hay muchas personas que tratan de evitar las expresiones regulares a toda costa. Intentan encontrar una forma de evitar las expresiones regulares y aceptan codificación adicional solo por el bien de ella, incluso si una expresión regular sería una solución más compacta.
¿Por qué las expresiones regulares se consideran tan controvertidas? ¿Hay malentendidos generalizados acerca de cómo funcionan? ¿O podría ser una creencia amplia de que las expresiones regulares son generalmente lentas?
" Expresiones regulares: ahora tienes dos problemas " es un excelente artículo de Jeff Atwood al respecto. Básicamente, las expresiones regulares son "duras"! Pueden crear nuevos problemas. Sin embargo, son eficaces.
Casi puedes estar preguntando por qué los goto son controvertidos.
Básicamente, cuando obtienes tanto poder "obvio", las personas están dispuestas a abusar de ellas en situaciones en las que no son la mejor opción. La cantidad de personas que solicitan analizar CSV o XML o HTML en expresiones regulares, por ejemplo, me sorprende. Es la herramienta equivocada para el trabajo. Pero algunos usuarios insisten en usar expresiones regulares de todos modos.
Personalmente, trato de encontrar ese medio feliz: use las expresiones regulares para lo que son buenas y evítelas cuando son menos que óptimas.
Tenga en cuenta que las expresiones regulares pueden seguir utilizándose para analizar CSV, XML, HTML, etc. Pero generalmente no en una expresión regular.
Casi todos los que conozco que usan expresiones regulares con regularidad (con un juego de palabras) provienen de un fondo Unix-ish en el que usan herramientas que tratan a los RE como construcciones de programación de primera clase, como grep, sed, awk y Perl. Como casi no hay una sobrecarga sintáctica para usar una expresión regular, su productividad aumenta mucho cuando lo hacen.
En contraste, los programadores que usan lenguajes en los cuales los RE son una biblioteca externa tienden a no considerar qué expresiones regulares pueden traer a la mesa. El "costo-tiempo" del programador es tan alto que a) los RE nunca aparecieron como parte de su entrenamiento, o b) no "piensan" en términos de RE y prefieren recurrir a patrones más familiares.
Creo que es una técnica menos conocida entre los programadores. Por lo tanto, no hay una amplia aceptación para ello. Y si tiene un gerente no técnico para revisar su código o revisar su trabajo, entonces una expresión regular es muy mala. Pasará horas escribiendo una expresión regular perfecta, y obtendrá pocas marcas para el módulo pensando que ha escrito tan pocas líneas de código. Además, como se dijo en otra parte, leer expresiones regulares es una tarea muy difícil.
Debido a que carecen de la herramienta de aprendizaje más popular en los IDE comúnmente aceptados: No hay Regex Wizard. Ni siquiera autocompletado. Tienes que codificar todo el asunto por ti mismo.
El mejor uso válido y normal para expresiones regulares es para la validación del formato de la dirección de correo electrónico.
Esa es una buena aplicación de ello.
He usado las expresiones regulares innumerables veces como una sola vez en TextPad para masajear archivos planos, crear archivos csv, crear sentencias de inserción SQL y ese tipo de cosas.
Las expresiones regulares bien escritas no deben ser demasiado lentas. Por lo general, las alternativas, como toneladas de llamadas a Reemplazar, son opciones mucho más lentas. Bien podría hacerlo en una sola pasada.
Muchas situaciones requieren expresiones exactamente regulares y nada más.
Reemplazar los caracteres especiales que no se imprimen con caracteres inocuos es otro buen uso.
Por supuesto, puedo imaginar que hay algunos códigos de base que abusan de las expresiones regulares en detrimento de la capacidad de mantenimiento. Nunca he visto eso yo mismo. De hecho, los revisores de código me han evitado por no usar las expresiones regulares lo suficiente.
El problema es que las expresiones regulares son potencialmente tan poderosas que puedes hacer cosas con ellas para las que deberías usar algo diferente.
Un buen programador debe saber dónde usarlos y dónde no. El ejemplo típico es analizar lenguajes no regulares (ver Decidir si un idioma es regular ).
Creo que no puedes equivocarte si al principio te restringes a expresiones regulares reales (sin extensiones). Algunas extensiones pueden hacer su vida un poco más fácil, pero si encuentra algo difícil de expresar como una expresión regular real , esto puede ser una indicación de que una expresión regular no es la herramienta correcta.
En algunos casos creo que tienes que usarlos. Por ejemplo, para construir un lexer.
En mi opinión, este es un punto de vista de las personas que pueden escribir expresiones regulares y las personas que no lo hacen (o apenas). Personalmente, esta es una buena idea, por ejemplo, para validar la entrada de un formulario, ya sea en javascript para advertir al usuario, o en lenguaje del lado del servidor.
Encuentro expresiones regulares invaluables a veces. Cuando necesito hacer algunas búsquedas "difusas", y tal vez reemplaza. Cuando los datos pueden variar y tener una cierta aleatoriedad. Sin embargo, cuando necesito hacer una búsqueda simple y reemplazar, o buscar una cadena, no uso expresiones regulares. Aunque conozco a mucha gente que sí, lo usan para todo. Esa es la controversia.
Si quieres poner una tachuela en la pared, no uses un martillo. Sí, funcionará, pero para cuando consigas el martillo, podría poner 20 tachuelas en la pared.
Las expresiones regulares deben usarse para lo que fueron diseñadas, y nada menos.
Este es un tema interesante.
Muchos aficionados a las expresiones regulares parecen confundir la concisión de la fórmula con la eficiencia.
Además de eso, una expresión regular que requiere mucho pensamiento produce a su autor una satisfacción masiva que la legitima de inmediato.
Pero ... las expresiones regulares son muy convenientes cuando el rendimiento no es un problema y necesita tratar rápidamente una salida de texto, en Perl, por ejemplo. Además, si bien el rendimiento es un problema, es posible que prefiera no intentar superar la biblioteca regexp utilizando un algoritmo casero que puede tener errores o ser menos eficiente.
Además, hay una serie de razones por las cuales las expresiones regulares son criticadas injustamente, por ejemplo
- la expresión regular no es eficiente, porque construir la superior no es obvio
- algunos programadores "olvidan" compilar solo una vez una expresión regular para ser utilizada muchas veces (como un patrón estático en Java)
- Algunos programadores optan por la estrategia de prueba y error : ¡funciona incluso menos con las expresiones regulares!
La gente tiende a pensar que las expresiones regulares son difíciles; pero eso es porque los están usando mal. Escribir líneas complejas complejas sin comentarios, sangría o capturas con nombre. (No coloque su expresión SQL compleja en una línea, sin comentarios, sangría o alias, ¿verdad?). Así que sí, para mucha gente, no tienen sentido.
Sin embargo, si su trabajo tiene algo que ver con el análisis de texto (aproximadamente cualquier aplicación web por ahí ...) y no conoce expresiones regulares, apesta su trabajo y está perdiendo su propio tiempo y el de su trabajo. empleador. Hay excelentes recursos para enseñarte todo sobre ellos que siempre necesitarás saber, y más.
Las expresiones regulares le permiten escribir una máquina de estado finito (FSM) personalizada de manera compacta, para procesar una cadena de entrada. Hay al menos dos razones por las que usar expresiones regulares es difícil:
El desarrollo de software de la vieja escuela implica mucha planificación, modelos de papel y una cuidadosa reflexión. Las expresiones regulares encajan muy bien en este modelo, porque escribir una expresión efectiva de manera adecuada implica mirarla mucho y visualizar las rutas del FSM.
Los desarrolladores de software modernos preferirían forzar código, y usar un depurador para pasar a través de la ejecución, para ver si el código es correcto. Las expresiones regulares no soportan muy bien este estilo de trabajo. Una "ejecución" de una expresión regular es efectivamente una operación atómica. Es difícil observar la ejecución paso a paso en un depurador.
Es demasiado fácil escribir una expresión regular que accidentalmente acepta más información de la que pretendes. El valor de una expresión regular no es realmente para que coincida con una entrada válida, es para que no coincida con una entrada no válida . Las técnicas para hacer "pruebas negativas" para expresiones regulares no son muy avanzadas, o al menos no se usan ampliamente.
Esto va al punto de que las expresiones regulares son difíciles de leer. Con solo mirar una expresión regular, se necesita mucha concentración para visualizar todas las entradas posibles que deberían rechazarse, pero que son aceptadas erróneamente. ¿Alguna vez intentaste depurar el código de expresión regular de otra persona ?
Si existe una resistencia al uso de expresiones regulares entre los desarrolladores de software hoy en día, creo que se debe principalmente a estos dos factores.
Las expresiones regulares son para las cadenas, lo que los operadores aritméticos son para los números, y no los consideraría controvertidos. Creo que incluso a un activista de OO bastante militante como yo (que tendería a elegir otros objetos sobre cadenas) sería difícil rechazarlos.
Las expresiones regulares son un misterio serio para muchas personas, incluido yo mismo. Funciona muy bien pero es como mirar una ecuación matemática. Sin embargo, me complace informar que alguien finalmente ha creado una ubicación consolidada de varias funciones de expresión regular en http://regexlib.com/ . Ahora, si Microsoft solo creara una clase de expresión regular que hiciera automáticamente muchas de las cosas comunes como eliminar letras o filtrar fechas.
Lo que creo es que Learning Regex y mantener las expresiones regulares en impopulares, la mayoría de los desarrolladores son perezosos o la mayoría de ellos confían en bibliotecas externas para hacer el análisis por ellos ... confían en google para la respuesta e incluso preguntan en foros para El código completo para su problema. Pero cuando se trata de implementar o modificar / mantener una expresión regular, simplemente fallan.
Hay un dicho popular: "Los amigos no permiten que los amigos usen Regex para analizar HTML"
Pero, en lo que a mí respecta, he hecho parsers HTML completos usando Regex y creo que los regex son mejores para analizar las cadenas html tanto en la velocidad como en la memoria (si tienes una idea de lo que quieres lograr).
Los Regexes son una gran herramienta, pero la gente piensa "¡Eh, qué gran herramienta, la usaré para hacer X!" donde X es algo para lo que es mejor una herramienta diferente (generalmente un analizador). Es el estándar con un martillo donde necesita un problema de destornillador.
Los sistemas de expresiones regulares decentes, como los utilizados en lex y yacc para la definición del compilador, son buenos, muy útiles y limpios. En estos sistemas, los tipos de expresión se definen en términos de otros. Son las expresiones regulares de una sola línea del ruido de línea ilegible, malformadas, ilegibles que se encuentran comúnmente en los códigos perl y sed (etc.) que son "controvertidas" (basura).
No creo que "controversial" sea la palabra correcta.
Pero he visto muchos ejemplos en los que la gente dice "¿cuál es la expresión regular que necesito para hacer tal y tal manipulación de cuerdas?" cuales son los problemas de XY
En otras palabras, partieron del supuesto de que una expresión regular es lo que necesitan, pero estarían mejor con una división (), una traducción como tr /// de perl donde los caracteres se sustituyen uno por el otro, o sólo un índice ().
No creo que la gente se oponga a las expresiones regulares porque son lentas, sino porque son difíciles de leer y escribir, así como difíciles de acertar. Si bien hay algunas situaciones en las que las expresiones regulares proporcionan una solución efectiva y compacta al problema, a veces se convierten en situaciones en las que es mejor usar una sección de código fácil de leer y fácil de mantener.
No creo que sean tan controvertidos.
También creo que de alguna manera ha respondido a su propia pregunta, porque señala cuán tonto sería usarlos en todas partes ( no todo es un lenguaje regular 2 ) o evitar usarlos en absoluto. Usted, el programador, tiene que tomar una decisión inteligente sobre cuándo las expresiones regulares ayudarán al código o lo dañarán. Cuando nos enfrentamos a una decisión de este tipo, dos cosas importantes a tener en cuenta son la capacidad de mantenimiento (lo que implica legibilidad) y la extensibilidad.
Para aquellos que son particularmente reacios a ellos, supongo que nunca han aprendido a usarlos correctamente. Creo que la mayoría de las personas que pasan unas pocas horas con un tutorial decente las entenderán y serán fluidas muy rápidamente. Aquí está mi sugerencia de por dónde empezar:
http://docs.python.org/howto/regex
Aunque esa página habla de expresiones regulares en el contexto de Python, he encontrado que la información es muy aplicable en otros lugares. Hay algunas cosas que son específicas de Python, pero creo que están claramente anotadas y son fáciles de recordar.
Si bien creo que las expresiones regulares son una herramienta esencial, lo más molesto de ellas es que existen diferentes implementaciones. Las pequeñas diferencias en la sintaxis, los modificadores y, especialmente, la "codicia" pueden hacer que las cosas sean realmente caóticas, que requieran prueba y error y, en ocasiones, generen errores desconcertantes.