Prospettiva storica
L'articolo di Wikipedia è abbastanza dettagliato sulle origini delle espressioni regolari (Kleene, 1956). La sintassi originale era relativamente semplice con solo *
, +
, ?
, |
e il raggruppamento (...)
. Era conciso ( e leggibile, i due non sono necessariamente opposti), perché i linguaggi formali tendono ad essere espressi con concise notazioni matematiche.
Più tardi, la sintassi e le capacità si sono evolute con gli editor e sono cresciute con Perl , che stava cercando di essere conciso dal design ( "le costruzioni comuni dovrebbero essere brevi" ). Ciò ha complicato molto la sintassi, ma nota che le persone sono ormai abituate alle espressioni regolari e sono brave a scriverle (se non a leggerle). Il fatto che a volte siano di sola scrittura suggeriscono che quando sono troppo lunghi, generalmente non sono lo strumento giusto.
Le espressioni regolari tendono ad essere illeggibili quando vengono abusate.
Oltre le espressioni regolari basate su stringhe
Parlando di sintassi alternative, diamo un'occhiata a una già esistente ( cl-ppcre , in Common Lisp ). La tua espressione regolare lunga può essere analizzata ppcre:parse-string
come segue:
(let ((*print-case* :downcase)
(*print-right-margin* 50))
(pprint
(ppcre:parse-string "^(?:([A-Za-z]+):)?(\\/{0,3})(0-9.\\-A-Za-z]+)(?::(\\d+))?(?:\\/([^?#]*))?(?:\\?([^#]*))?(?:#(.*))?$")))
... e risulta nella seguente 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)
Questa sintassi è più dettagliata e, se si osservano i commenti di seguito, non necessariamente più leggibile. Quindi non dare per scontato che, poiché hai una sintassi meno compatta, le cose saranno automaticamente più chiare .
Tuttavia, se inizi a riscontrare problemi con le tue espressioni regolari, trasformarle in questo formato potrebbe aiutarti a decifrare ed eseguire il debug del codice. Questo è un vantaggio rispetto ai formati basati su stringhe, in cui un singolo carattere può essere difficile da individuare.
Il vantaggio principale di questa sintassi è manipolare le espressioni regolari usando un formato strutturato anziché una codifica basata su stringhe. Ciò ti consente di comporre e costruire espressioni del genere come qualsiasi altra struttura di dati nel tuo programma. Quando uso la sintassi sopra, questo è generalmente perché voglio creare espressioni da parti più piccole (vedi anche la mia risposta CodeGolf ). Per il tuo esempio, possiamo scrivere 1 :
`(:sequence
:start-anchor
,(protocol)
,(slashes)
,(domain)
,(top-level-domain) ... )
Le espressioni regolari basate su stringa possono anche essere composte, usando la concatenazione di stringhe o l'interpolazione racchiusa in funzioni di supporto. Tuttavia, ci sono limitazioni con le manipolazioni delle stringhe che tendono a ingombrare il codice (pensa ai problemi di annidamento, non diversamente dai backtick rispetto $(...)
a bash; inoltre, i caratteri di escape possono farti venire il mal di testa).
Si noti inoltre che il modulo sopra consente i (:regex "string")
moduli in modo da poter mescolare notazioni concise con alberi. Tutto ciò porta l'IMHO a una buona leggibilità e compostabilità; affronta i tre problemi espressi da delnan , indirettamente (cioè non nella lingua delle espressioni regolari stesse).
Concludere
Per molti scopi, la notazione concisa è in effetti leggibile. Ci sono difficoltà nel trattare le notazioni estese che implicano il backtracking, ecc., Ma il loro uso è raramente giustificato. L'uso ingiustificato di espressioni regolari può portare a espressioni illeggibili.
Le espressioni regolari non devono essere codificate come stringhe. Se hai una libreria o uno strumento che può aiutarti a costruire e comporre espressioni regolari, eviterai molti potenziali bug relativi alle manipolazioni delle stringhe.
In alternativa, le grammatiche formali sono più leggibili e sono migliori nella denominazione e nell'astrazione di sottoespressioni. I terminali sono generalmente espressi come semplici espressioni regolari.
1. Puoi preferire costruire le tue espressioni al momento della lettura, perché le espressioni regolari tendono ad essere costanti in un'applicazione. Vedi create-scanner
e load-time-value
:
'(:sequence :start-anchor #.(protocol) #.(slashes) ... )