Perspectiva histórica
O artigo da Wikipedia é bastante detalhado sobre as origens das expressões regulares (Kleene, 1956). A sintaxe original era relativamente simples, com apenas *
, +
, ?
, |
e agrupamento (...)
. Foi conciso ( e legível, os dois não são necessariamente opostos), porque as línguas formais tendem a ser expressas com notações matemáticas concisas.
Mais tarde, a sintaxe e os recursos evoluíram com editores e cresceram com o Perl , que estava tentando ser conciso pelo design ( "construções comuns devem ser curtas" ). Isso complexificou muito a sintaxe, mas observe que as pessoas agora estão acostumadas a expressões regulares e são boas em escrever (se não as lerem). O fato de que às vezes eles são escritos apenas sugere que, quando são muito longos, geralmente não são a ferramenta certa.
Expressões regulares tendem a ser ilegíveis quando são abusadas.
Além das expressões regulares baseadas em string
Falando sobre sintaxes alternativas, vamos dar uma olhada em uma que já existe ( cl-ppcre , em Common Lisp ). Sua longa expressão regular pode ser analisada com ppcre:parse-string
da seguinte maneira:
(let ((*print-case* :downcase)
(*print-right-margin* 50))
(pprint
(ppcre:parse-string "^(?:([A-Za-z]+):)?(\/{0,3})(0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$")))
... e resulta da seguinte forma:
(: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 sintaxe é mais detalhada e, se você observar os comentários abaixo, não é necessariamente mais legível. Então não assuma que porque você tem uma sintaxe menos compacta, as coisas serão automaticamente mais claras .
No entanto, se você começar a ter problemas com suas expressões regulares, transformá-las nesse formato poderá ajudá-lo a decifrar e depurar seu código.
Essa é uma vantagem sobre os formatos baseados em seqüência de caracteres, em que um erro de caractere único pode ser difícil de detectar.
A principal vantagem desta sintaxe é manipular expressões regulares usando um formato estruturado em vez de uma codificação baseada em string. Isso permite a você compor e construir expressões como qualquer outra estrutura de dados em seu programa.
Quando eu uso a sintaxe acima, geralmente é porque eu quero construir expressões de partes menores (veja também minha resposta ao CodeGolf ). Para o seu exemplo, podemos escrever 1 :
'(:sequence
:start-anchor
,(protocol)
,(slashes)
,(domain)
,(top-level-domain) ... )
Expressões regulares baseadas em strings também podem ser compostas, usando concatenação de strings e / ou interpolações envolvidas em funções auxiliares. No entanto, existem limitações nas manipulações de strings que tendem a a desordem do code (pense em problemas de aninhamento, não diferente de backticks vs. $(...)
no bash; além disso, caracteres de escape podem causar dores de cabeça).
Note também que o formulário acima permite (:regex "string")
forms para que você possa misturar notações com árvores. Tudo isso leva a IMHO a uma boa legibilidade e composibilidade; ele aborda os três problemas expressos por delnan , indiretamente (ou seja, não na própria linguagem das expressões regulares).
Para concluir
-
Para o propósito, a notação concisa é de fato legível. Há dificuldades ao lidar com notações estendidas que envolvem retrocessos, etc., mas raramente são justificadas. O uso indevido de expressões regulares pode levar a expressões ilegíveis.
-
Expressões regulares não precisam ser codificadas como strings. Se você tiver uma biblioteca ou uma ferramenta que possa ajudá-lo a criar e compor expressões regulares, você evitará muitos bugs em potencial relacionados a manipulações de strings.
-
Como alternativa, as gramáticas formais são mais legíveis e são melhores para nomear e abstrair subexpressões. Os terminais são geralmente expressos como expressões regulares simples.
1. Você pode preferir criar suas expressões em tempo de leitura, porque expressões regulares tendem a ser constantes em um aplicativo. Consulte create-scanner
e load-time-value
:
'(:sequence :start-anchor #.(protocol) #.(slashes) ... )