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 terso ( e leggibile, i due non sono necessariamente opposti), perché i linguaggi formali tendono ad essere espressi con notazioni matematiche concise.
Successivamente, la sintassi e le funzionalità si sono evolute con gli editor e sono cresciute con Perl , che cercava di essere conciso in base al design ( "le costruzioni comuni dovrebbero essere brevi" ). Questo ha complicato molto la sintassi, ma si noti che le persone sono ora abituate alle espressioni regolari e sono brave a scrivere (se non a leggerle). Il fatto che siano a volte solo di scrittura suggeriscono che quando sono troppo lunghi, in genere non sono lo strumento giusto.
Le espressioni regolari tendono a essere illeggibili quando vengono utilizzate abusivamente.
Al di là delle espressioni regolari basate su stringhe
Parlando di sintassi alternative, diamo un'occhiata a quella già esistente ( cl-ppcre , in Common Lisp ). La tua lunga espressione regolare può essere analizzata con 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 restituisce il seguente modulo:
(: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ù prolissa e se si guardano i commenti sotto, non necessariamente più leggibili. 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 espressioni regolari, trasformarle in questo formato potrebbe aiutarti a decifrare e eseguire il debug del codice.
Questo è uno dei vantaggi rispetto ai formati basati su stringhe, in cui un errore di singolo carattere può essere difficile da individuare.
Il vantaggio principale di questa sintassi è di manipolare le espressioni regolari utilizzando un formato strutturato anziché una codifica basata su stringhe. Ciò ti consente di comporre e creare tali espressioni 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 mia risposta CodeGolf ). Per il tuo esempio, potremmo scrivere 1 :
'(:sequence
:start-anchor
,(protocol)
,(slashes)
,(domain)
,(top-level-domain) ... )
Le espressioni regolari basate su stringhe possono anche essere composte, usando la concatenazione di stringhe o l'interpolazione racchiusa nelle funzioni di supporto. Tuttavia, ci sono limitazioni con le manipolazioni di stringhe che tendono a ingombrare il code (pensa ai problemi di annidamento, non diversamente dai backtick rispetto a $(...)
in bash; inoltre, i caratteri di escape possono darti mal di testa).
Nota anche che il modulo sopra permette forme (:regex "string")
in modo da poter mescolare le notazioni concise con gli alberi. Tutto ciò porta IMHO a una buona leggibilità e componibilità; indirizza i tre problemi espressi da delnan , indirettamente (cioè non nella lingua delle stesse espressioni regolari).
Per concludere
-
Per la maggior parte degli scopi, la notazione tersa è in realtà leggibile. Ci sono difficoltà quando si ha a che fare con 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 ti può aiutare a costruire e comporre espressioni regolari, eviti molti potenziali bug relativi alle manipolazioni delle stringhe.
-
In alternativa, le grammatiche formali sono più leggibili e sono migliori nel nominare e astrarre le sotto-espressioni. I terminali sono generalmente espressi come semplici espressioni regolari.
1. Potresti preferire creare le espressioni in fase di lettura, poiché le espressioni regolari tendono a essere costanti in un'applicazione. Vedi create-scanner
e load-time-value
:
'(:sequence :start-anchor #.(protocol) #.(slashes) ... )