Perspectiva histórica
O artigo da Wikipedia é bastante detalhado sobre as origens das expressões regulares (Kleene, 1956). A sintaxe original relativamente simples, com apenas *
, +
, ?
, |
e agrupamento (...)
. Era conciso ( e legível, os dois não são necessariamente opostos), porque as linguagens formais tendem a ser expressas com notações matemáticas concisas.
Mais tarde, a sintaxe e os recursos evoluíram com os editores e cresceram com o Perl , que estava tentando ser conciso pelo design ( "construções comuns devem ser curtas" ). Isso complexou bastante a sintaxe, mas observe que as pessoas agora estão acostumadas a expressões regulares e sabem escrever (se não estiverem lendo). O fato de às vezes serem apenas de gravação 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 expressão regular longa pode ser analisada da ppcre:parse-string
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)
Essa sintaxe é mais detalhada e, se você observar os comentários abaixo, não é necessariamente mais legível. Portanto, não presuma que, como você possui 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 pode ajudá-lo a decifrar e depurar seu código. Essa é uma vantagem sobre os formatos baseados em cadeias, onde um erro de um único caractere pode ser difícil de detectar.
A principal vantagem dessa sintaxe é manipular expressões regulares usando um formato estruturado em vez de uma codificação baseada em string. Isso permite que você componha e construa expressões como qualquer outra estrutura de dados em seu programa. Quando eu uso a sintaxe acima, isso geralmente ocorre porque eu quero construir expressões de partes menores (veja também minha resposta do CodeGolf ). Para seu exemplo, podemos escrever 1 :
`(:sequence
:start-anchor
,(protocol)
,(slashes)
,(domain)
,(top-level-domain) ... )
Expressões regulares baseadas em string também podem ser compostas, usando concatenação e / ou interpolação de string agrupadas em funções auxiliares. No entanto, existem limitações nas manipulações de strings que tendem a confundir o código (pense em problemas de aninhamento, não muito diferentes de backticks vs. $(...)
bash; também, caracteres de escape podem causar dores de cabeça).
Observe também que o formulário acima permite (:regex "string")
formulários para que você possa misturar notações concisas com árvores. Tudo isso leva o IMHO a boa legibilidade e composição; aborda os três problemas expressos por delnan , indiretamente (ou seja, não na linguagem das expressões regulares).
Concluir
Para a maioria dos propósitos, a notação concisa é de fato legível. Existem dificuldades ao lidar com notações estendidas que envolvem retorno, etc., mas raramente são justificadas. O uso injustificado de expressões regulares pode levar a expressões ilegíveis.
Expressões regulares não precisam ser codificadas como seqüências de caracteres. Se você tem uma biblioteca ou uma ferramenta que pode ajudá-lo a criar e compor expressões regulares, você evitará muitos bugs em potencial relacionados à manipulação de strings.
Alternativamente, as gramáticas formais são mais legíveis e são melhores para nomear e abstrair sub-expressõ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. Veja create-scanner
e load-time-value
:
'(:sequence :start-anchor #.(protocol) #.(slashes) ... )