Perspectiva histórica
El artículo de Wikipedia es bastante detallado sobre los orígenes de las expresiones regulares (Kleene, 1956). La sintaxis original era relativamente simple con solo *
, +
, ?
, |
y agrupación (...)
. Era conciso ( y legibles, los dos no son necesariamente opuestos), porque los lenguajes formales tienden a expresarse con notaciones matemáticas concisas.
Más tarde, la sintaxis y las capacidades evolucionaron con los editores y crecieron con Perl , que intentaba ser concisa por diseño ( "las construcciones comunes deben ser cortas" ). Esto complejizó mucho la sintaxis, pero tenga en cuenta que las personas ahora están acostumbradas a las expresiones regulares y son buenas para escribirlas (si no leerlas). El hecho de que a veces son de solo escritura sugiere que cuando son demasiado largos, generalmente no son la herramienta adecuada.
Las expresiones regulares tienden a ser ilegibles cuando se abusa de ellas.
Más allá de las expresiones regulares basadas en cadenas
Hablando de sintaxis alternativas, echemos un vistazo a una que ya existe ( cl-ppcre , en Common Lisp ). Su expresión regular larga se puede analizar con ppcre:parse-string
de la siguiente manera:
(let ((*print-case* :downcase)
(*print-right-margin* 50))
(pprint
(ppcre:parse-string "^(?:([A-Za-z]+):)?(\/{0,3})(0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$")))
... y da como resultado el siguiente formulario:
(:sequence :start-anchor
(:greedy-repetition 0 1
(:group
(:sequence
(:register
(:greedy-repetition 1 nil
(:char-class (:range #\A #\Z)
(:range #\a #\z))))
#\:)))
(:register (:greedy-repetition 0 3 #\/))
(:register
(:sequence "0-9" :everything "-A-Za-z"
(:greedy-repetition 1 nil #\])))
(:greedy-repetition 0 1
(:group
(:sequence #\:
(:register
(:greedy-repetition 1 nil :digit-class)))))
(:greedy-repetition 0 1
(:group
(:sequence #\/
(:register
(:greedy-repetition 0 nil
(:inverted-char-class #\? #\#))))))
(:greedy-repetition 0 1
(:group
(:sequence #\?
(:register
(:greedy-repetition 0 nil
(:inverted-char-class #\#))))))
(:greedy-repetition 0 1
(:group
(:sequence #\#
(:register
(:greedy-repetition 0 nil :everything)))))
:end-anchor)
Esta sintaxis es más detallada y, si observa los comentarios a continuación, no necesariamente será más legible. Así que no asumas que debido a que tienes una sintaxis menos compacta, las cosas se aclararán automáticamente .
Sin embargo, si comienza a tener problemas con sus expresiones regulares, convertirlas en este formato podría ayudarlo a descifrar y depurar su código.
Esta es una ventaja sobre los formatos basados en cadenas, donde un error de un solo carácter puede ser difícil de detectar.
La principal ventaja de esta sintaxis es manipular expresiones regulares mediante un formato estructurado en lugar de una codificación basada en cadenas. Eso le permite componer y construir tales expresiones como cualquier otra estructura de datos en su programa.
Cuando uso la sintaxis anterior, esto generalmente se debe a que quiero compilar expresiones de partes más pequeñas (vea también mi respuesta de CodeGolf ). Para su ejemplo, podemos escribir 1 :
'(:sequence
:start-anchor
,(protocol)
,(slashes)
,(domain)
,(top-level-domain) ... )
Las expresiones regulares basadas en cadenas también se pueden componer, usando concatenación de cadenas o interpolación envueltas en funciones auxiliares. Sin embargo, existen limitaciones con la manipulación de cadenas que tienden a clutter the code (piense en los problemas de anidamiento, no a diferencia de backticks vs. $(...)
en bash; también, los caracteres de escape pueden provocarle dolores de cabeza).
Tenga en cuenta también que la forma anterior permite que (:regex "string")
forme para que pueda mezclar notaciones concisas con árboles. Todo eso lleva a IMHO a una buena legibilidad y composibilidad; aborda los tres problemas expresados por delnan , indirectamente (es decir, no en el lenguaje de las expresiones regulares en sí).
Para concluir
-
Para la mayoría de los propósitos, la notación concisa es, de hecho, legible. Hay dificultades cuando se trata de notaciones extendidas que involucran el retroceso, etc., pero su uso rara vez se justifica. El uso injustificado de expresiones regulares puede llevar a expresiones ilegibles.
-
Las expresiones regulares no necesitan codificarse como cadenas. Si tienes una biblioteca o una herramienta que te puede ayudar a construir y componer expresiones regulares, evitarás muchos posibles errores relacionados con la manipulación de cadenas.
-
Alternativamente, las gramáticas formales son más legibles y son mejores para nombrar y abstraer las sub-expresiones. Los terminales generalmente se expresan como simples expresiones regulares.
1. Es posible que prefieras construir tus expresiones en tiempo de lectura, porque las expresiones regulares tienden a ser constantes en una aplicación. Consulte create-scanner
y load-time-value
:
'(:sequence :start-anchor #.(protocol) #.(slashes) ... )