Perché le espressioni regolari sono così controverse?

Quando si esplorano le espressioni regolari (altrimenti noti come RegEx-es), ci sono molte persone che sembrano vedere espressioni regolari come il Santo Graal. Qualcosa che sembra così complicato, deve essere la risposta a qualsiasi domanda. Tendono a pensare che ogni problema sia risolvibile usando espressioni regolari.

D’altra parte, ci sono anche molte persone che cercano di evitare le espressioni regolari a tutti i costi. Cercano di trovare un modo per aggirare le espressioni regolari e accettano codifiche aggiuntive solo per il gusto di farlo, anche se un’espressione regolare sarebbe una soluzione più compatta.

Perché le espressioni regolari sono considerate così controverse? Ci sono malintesi diffusi su come funzionano? O potrebbe essere una convinzione generale che le espressioni regolari siano generalmente lente?

Non penso che le persone si oppongano alle espressioni regolari perché sono lente, ma piuttosto perché sono difficili da leggere e scrivere, oltre che difficili da ottenere. Mentre ci sono alcune situazioni in cui le espressioni regolari forniscono una soluzione efficace e compatta al problema, a volte vengono caricate in situazioni in cui è preferibile utilizzare una sezione di codice di facile lettura e manutenibile.

Rendere Regexes mantenibile

Un importante progresso verso demistificare i modelli precedentemente definiti come “espressioni regolari” è il flag di espressione regolare di Perl /x – a volte scritto (?x) quando incorporato – che consente spazio bianco (interruzione di riga, rientro) e commenti. Questo migliora seriamente la leggibilità e quindi la manutenibilità. Lo spazio bianco consente il chunking cognitivo, quindi puoi vedere quali gruppi con cosa.

I modelli moderni ora supportano anche le backreferenze numerate e con nome. Ciò significa che non è più necessario contare i gruppi di acquisizione per capire che è necessario $4 o \7 . Questo aiuta nella creazione di modelli che possono essere inclusi in ulteriori modelli.

Ecco un esempio di un gruppo di acquisizione relativamente numerato:

 $ dupword = qr {\ b (?: (\ w +) (?: \ s + \ g {-1}) +) \ b} xi;
 $ quoted = qr {(["']) $ dupword \ 1} x;

Ed ecco un esempio dell’approccio superiore delle acquisizioni nominate:

 $dupword = qr{ \b (?: (? \w+ ) (?: \s+ \k )+ ) \b }xi; $quoted = qr{ (? ["'] ) $dupword \g{quote} }x; 

Regex grammaticali

Meglio di tutti , queste catture nominate possono essere inserite all’interno di un blocco (?(DEFINE)...) , in modo da poter separare la dichiarazione dall’esecuzione dei singoli elementi nominati dei tuoi schemi. Questo li rende piuttosto simili a subroutine all’interno del pattern.
Un buon esempio di questo tipo di “regex grammaticale” può essere trovato in questa risposta e questa . Questi sembrano molto più simili a una dichiarazione grammaticale.

Come quest’ultimo ti ricorda:

… assicurati di non scrivere mai schemi di rumore di linea. Non devi, e non dovresti. Non è ansible mantenere alcun linguaggio di programmazione che impedisca lo spazio bianco, i commenti, le subroutine o gli identificatori alfanumerici. Quindi usa tutte quelle cose nei tuoi schemi.

Questo non può essere troppo enfatizzato. Ovviamente se non usi queste cose nei tuoi schemi, creerai spesso un incubo. Ma se li usi, però, non è necessario.

Ecco un altro esempio di un modello grammaticale moderno, questo per analizzare RFC 5322: usare 5.10.0;

 $rfc5322 = qr{ (?(DEFINE) (?
(?&mailbox) | (?&group)) (? (?&name_addr) | (?&addr_spec)) (? (?&display_name)? (?&angle_addr)) (? (?&CFWS)? < (?&addr_spec) > (?&CFWS)?) (? (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ; (?&CFWS)?) (? (?&phrase)) (? (?&mailbox) (?: , (?&mailbox))*) (? (?&local_part) \@ (?&domain)) (? (?&dot_atom) | (?&quoted_string)) (? (?&dot_atom) | (?&domain_literal)) (? (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)? \] (?&CFWS)?) (? (?&dtext) | (?&quoted_pair)) (? (?&NO_WS_CTL) | [\x21-\x5a\x5e-\x7e]) (? (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~]) (? (?&CFWS)? (?&atext)+ (?&CFWS)?) (? (?&CFWS)? (?&dot_atom_text) (?&CFWS)?) (? (?&atext)+ (?: \. (?&atext)+)*) (? [\x01-\x09\x0b\x0c\x0e-\x7f]) (? \\ (?&text)) (? (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e]) (? (?&qtext) | (?&quoted_pair)) (? (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))* (?&FWS)? (?&DQUOTE) (?&CFWS)?) (? (?&atom) | (?&quoted_string)) (? (?&word)+) # Folding white space (? (?: (?&WSP)* (?&CRLF))? (?&WSP)+) (? (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e]) (? (?&ctext) | (?&quoted_pair) | (?&comment)) (? \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) ) (? (?: (?&FWS)? (?&comment))* (?: (?:(?&FWS)? (?&comment)) | (?&FWS))) # No whitespace control (? [\x01-\x08\x0b\x0c\x0e-\x1f\x7f]) (? [A-Za-z]) (? [0-9]) (? \x0d \x0a) (? ") (? [\x20\x09]) ) (?&address) }x;

Non è straordinario – e splendido? Puoi prendere una grammatica in stile BNF e tradurla direttamente in codice senza perdere la sua struttura fondamentale!

Se i moderni schemi grammaticali non ti bastano, allora il brillante modulo Regexp::Grammars Damian Conway offre una syntax ancora più pulita, con un debugging superiore. Ecco lo stesso codice per analizzare la rifusione di RFC 5322 in uno schema da quel modulo:

 #!/usr/bin/perl use strict; use warnings; use 5.010; use Data::Dumper "Dumper"; my $rfc5322 = do { use Regexp::Grammars; # ...the magic is lexically scoped qr{ # Keep the big stick handy, just in case... #  # Match this... 
# As defined by these... | | ? ? \< \> ? : (?: | )? ; ? < [mailbox]> ** (,) \@ | | ? \[ (?: ? < [dcontent]>)* ? | < .NO_WS_CTL> | [\x21-\x5a\x5e-\x7e] < .ALPHA> | < .DIGIT> | [!#\$%&'*+-/=?^_`{|}~] < .CFWS>? < .atext>+ < .CFWS>? < .CFWS>? < .dot_atom_text> < .CFWS>? < .CFWS>? < .dot_atom_text> < .CFWS>? < .atext>+ (?: \. < .atext>+)* [\x01-\x09\x0b\x0c\x0e-\x7f] \\ < .text> < .NO_WS_CTL> | [\x21\x23-\x5b\x5d-\x7e] < .qtext> | < .quoted_pair> < .CFWS>? < .DQUOTE> (?:< .FWS>? < .qcontent>)* < .FWS>? < .DQUOTE> < .CFWS>? < .atom> | < .quoted_string> < .word>+ # Folding white space (?: < .WSP>* < .CRLF>)? < .WSP>+ < .NO_WS_CTL> | [\x21-\x27\x2a-\x5b\x5d-\x7e] < .ctext> | < .quoted_pair> | < .comment> \( (?: < .FWS>? < .ccontent>)* < .FWS>? \) (?: < .FWS>? < .comment>)* (?: (?:< .FWS>? < .comment>) | < .FWS>) # No whitespace control [\x01-\x08\x0b\x0c\x0e-\x1f\x7f] [A-Za-z] [0-9] \x0d \x0a " [\x20\x09] }x; }; while (my $input = <>) { if ($input =~ $rfc5322) { say Dumper \%/; # ...the parse tree of any successful match # appears in this punctuation variable } }

Ci sono molte cose buone nella pagina di manuale di perlre , ma questi notevoli miglioramenti nelle caratteristiche fondamentali del design regex non sono affatto limitati al solo Perl. In effetti la pagina di manuale di pcrepattern può essere più facile da leggere e copre lo stesso territorio.

I modelli moderni non hanno quasi nulla in comune con le cose primitive che ti sono state insegnate nella tua class di automi finiti.

I regex sono un ottimo strumento, ma la gente pensa “Ehi, che grande strumento, lo userò per fare X!” dove X è qualcosa per cui uno strumento diverso è migliore (di solito un parser). È lo standard che utilizza un martello dove è necessario un problema con un cacciavite.

Quasi tutti quelli che conosco che usano regolarmente le espressioni regolari (giochi di parole) provengono da uno sfondo Unix-ish in cui usano strumenti che trattano i RE come costrutti di programmazione di prima class, come grep, sed, awk e Perl. Dal momento che non c’è quasi nessun sovraccarico sintattico per usare un’espressione regolare, la loro produttività aumenta quando lo fanno.

Al contrario, i programmatori che utilizzano le lingue in cui le RE sono una libreria esterna tendono a non considerare quali espressioni regolari possono apportare alla tabella. Il programmatore “time-cost” è così alto che a) le RE non sono mai apparso come parte del loro allenamento, o b) non “pensano” in termini di RE e preferiscono ricorrere a schemi più familiari.

Le espressioni regolari consentono di scrivere una macchina a stati finiti (FSM) personalizzata in modo compatto, per elaborare una stringa di input. Ci sono almeno due motivi per cui l’uso delle espressioni regolari è difficile:

  • Lo sviluppo di software della vecchia scuola implica molta pianificazione, modelli cartacei e un’attenta riflessione. Le espressioni regolari si adattano molto bene a questo modello, perché scrivere un’espressione efficace correttamente implica molto fissarlo, visualizzando i percorsi dell’FSM.

    I moderni sviluppatori di software preferirebbero piuttosto martellare il codice e utilizzare un debugger per eseguire l’esecuzione, per vedere se il codice è corretto. Le espressioni regolari non supportano molto bene questo stile di lavoro. Una “esecuzione” di un’espressione regolare è effettivamente un’operazione atomica. È difficile osservare l’esecuzione graduale in un debugger.

  • È troppo facile scrivere un’espressione regolare che accetta accidentalmente più input di quanto tu intenda. Il valore di un’espressione regolare non è proprio quello di corrispondere all’ingresso valido, ma non riesce a far corrispondere l’input non valido . Le tecniche per fare “test negativi” per le espressioni regolari non sono molto avanzate, o almeno non ampiamente utilizzate.

    Questo va al punto che le espressioni regolari sono difficili da leggere. Solo osservando un’espressione regolare, ci vuole molta concentrazione per visualizzare tutti i possibili input che dovrebbero essere rifiutati, ma vengono accettati per errore. Hai mai provato a eseguire il debug del codice di espressione regolare di qualcun altro ?

Se c’è una resistenza ad usare le espressioni regolari tra gli sviluppatori di software oggi, penso che sia principalmente dovuto a questi due fattori.

Le persone tendono a pensare che le espressioni regolari siano difficili; ma è perché li stanno usando male. Scrittura di one-liner complessi senza commenti, indenting o named capture. (Non si stipula la tua complessa espressione SQL in una riga, senza commenti, indentazione o alias, vero?). Quindi sì, per molte persone, non hanno senso.

Tuttavia, se il tuo lavoro ha qualcosa a che fare con l’analisi del testo (più o meno qualsiasi applicazione web là fuori …) e non conosci un’espressione regolare, fai schifo al tuo lavoro e stai sprecando il tuo tempo e quello del tuo datore di lavoro. Ci sono risorse eccellenti là fuori per insegnarti tutto su di loro che avrai sempre bisogno di sapere, e altro ancora.

Perché mancano lo strumento di apprendimento più popolare negli IDE comunemente accettati: non esiste un Regex Wizard. Neanche l’autocompletamento. Devi codificare tutto da solo.

Non penso che siano così controversi.

Penso anche che tu abbia risposto alla tua stessa domanda, perché fai notare quanto sarebbe sciocco usarli ovunque ( non tutto è un linguaggio normale 2 ) o evitare di usarli del tutto. Tu, il programmatore, devi prendere una decisione intelligente su quando le espressioni regolari possono aiutare il codice o danneggiarlo. Di fronte a tale decisione, due cose importanti da tenere a mente sono la manutenibilità (che implica la leggibilità) e l’estensibilità.

Per quelli che sono particolarmente avversi a loro, la mia ipotesi è che non hanno mai imparato ad usarli correttamente. Penso che la maggior parte delle persone che trascorrono solo poche ore con un tutorial decente le individuerà e diventerà fluente molto rapidamente. Ecco il mio suggerimento su dove iniziare:

http://docs.python.org/howto/regex

Sebbene quella pagina parli di espressioni regolari nel contesto di Python, ho trovato che le informazioni sono molto applicabili altrove. Ci sono alcune cose che sono specifiche di Python, ma credo che siano chiaramente annotate e facili da ricordare.

” Espressioni regolari: ora hai due problemi ” è un grande articolo di Jeff Atwood sull’argomento. Fondamentalmente, le espressioni regolari sono “difficili”! Possono creare nuovi problemi. Sono efficaci, comunque.

Le espressioni regolari sono le stringhe di ciò che gli operatori aritmetici fanno ai numeri e non li considererei controversi. Penso che anche un attivista OO abbastanza millimetrico come me (che tenderebbe a scegliere altri oggetti rispetto alle stringhe) sarebbe difficile rifiutarli.

Il problema è che le regex sono potenzialmente così potenti che puoi fare cose con loro per le quali dovresti usare qualcosa di diverso.

Un buon programmatore dovrebbe sapere dove usarli e dove no. L’esempio tipico è l’analisi di lingue non regolari (vedere Decidere se una lingua è regolare ).

Penso che non puoi sbagliare se inizialmente ti limiti a espressioni regolari reali (senza estensioni). Alcune estensioni possono rendere la vita un po ‘più facile, ma se trovi qualcosa di difficile da esprimere come una regex reale , questo potrebbe essere un’indicazione che una regex non è lo strumento giusto.

Quasi quasi starai chiedendo perché i goto sono controversi.

Fondamentalmente, quando ottieni così tanto potere “ovvio”, le persone sono abituate ad abusarne per situazioni che non rappresentano l’opzione migliore. Il numero di persone che chiedono di analizzare CSV o XML o HTML nelle regex, ad esempio, mi sbalordisce. È lo strumento sbagliato per il lavoro. Ma alcuni utenti insistono sull’uso delle regex comunque.

Personalmente, cerco di trovare quel mezzo felice – usa regex per quello per cui sono buoni, ed evitalo quando non sono ottimali.

Nota che le espressioni regolari possono ancora essere utilizzate per analizzare CSV, XML, HTML, ecc. Ma di solito non in una singola regex.

Non penso che “controverso” sia la parola giusta.

Ma ho visto un sacco di esempi in cui la gente dice “qual è l’espressione regolare di cui ho bisogno per fare una tale e una tale manipolazione delle stringhe?” quali sono i problemi XY.

In altre parole, sono partiti dal presupposto che una regex è ciò di cui hanno bisogno, ma starebbero meglio con uno split (), una traduzione come tr /// di perl in cui i caratteri sono sostituiti l’uno con l’altro, o solo un indice ().

Questo è un argomento interessante.
Molti aficionados regexp sembrano confondere la concisione della formula con l’efficienza.
Oltre a ciò, un’espressione regolare che richiede molto pensiero produce al suo autore una soddisfazione enorme che la rende immediatamente legittima.

Ma … le regex sono così convenienti quando le prestazioni non sono un problema ed è necessario occuparsi rapidamente di un output testuale, per esempio in Perl. Inoltre, mentre le prestazioni sono un problema, si potrebbe preferire non provare a battere la libreria regexp usando un algoritmo fatto in casa che può essere bacato o meno efficiente.

Inoltre ci sono una serie di ragioni per le quali le regex sono criticate ingiustamente, per esempio

  • la regexp non è efficiente, perché la costruzione della prima non è ovvia
  • alcuni programmatori “dimenticano” di compilare solo una volta un’espressione regolare da usare più volte (come un Pattern statico in Java)
  • alcuni programmatori optano per la strategia per tentativi ed errori – funziona ancora meno con espressioni regolari!

Quello che penso sia Learning Regex e il mantenimento della regex rende impopolare, la maggior parte degli sviluppatori sono pigri o la maggior parte di loro si affida alle librerie esterne per fare il parsing cosa per loro … si affidano a google per la risposta e anche chiedere nei forum per il codice completo per il loro problema. Ma quando arriva a implementare o modificare / mantenere una regex semplicemente falliscono.

C’è un detto popolare “Amici non lasciare che gli amici usino Regex per l’analisi dell’HTML”

Ma per quanto mi riguarda ho realizzato parser HTML completi usando Regex e ho scoperto che le espressioni regolari sono migliori nell’analisi delle stringhe html sia in termini di velocità che di memoria (se hai un’idea che cosa devi ottenere :))

Le espressioni regolari sono un serio mistero per molte persone, incluso me stesso. Funziona alla grande ma è come guardare un’equazione matematica. Sono felice di segnalare che qualcuno ha finalmente creato una posizione consolidata di varie funzioni di espressioni regolari su http://regexlib.com/ . Ora, se Microsoft creasse solo una class di espressioni regolari che farebbe automaticamente molte delle cose più comuni come eliminare le lettere o filtrare le date.

Trovo che le espressioni regolari siano inestimabili a volte. Quando ho bisogno di fare alcune ricerche “fuzzy”, e forse sostituisce. Quando i dati possono variare e avere una certa casualità. Tuttavia, quando ho bisogno di fare una semplice ricerca e sostituzione, o controllare una stringa, non uso espressioni regolari. Anche se conosco molte persone che lo usano, lo usano per tutto. Questa è la polemica.

Se vuoi mettere una virata nel muro, non usare un martello. Sì, funzionerà, ma quando arriverà il martello, potrei mettere 20 chiodi nel muro.

Le espressioni regolari dovrebbero essere utilizzate per ciò per cui sono state progettate, e niente di meno.

Ottieni RegexBuddy . Poi ti lanciare le espressioni regolari in giro come un professionista e come !! bonus !! inizi a capirli!

Mentre penso che le regex siano uno strumento essenziale, la cosa più fastidiosa di loro è che ci sono diverse implementazioni. Lievi differenze di syntax, modificatori e, soprattutto, “avidità” possono rendere le cose veramente caotiche, richiedendo tentativi ed errori e talvolta generando bug sconcertanti.

In alcuni casi penso che tu debba usarli. Ad esempio per build un lexer.

Secondo me, questo è un punto di vista di persone che possono scrivere regexp e persone che non lo fanno (o quasi). Personalmente penso che questo sia un buon modo per esempio per validare l’input di un modulo, sia in javascript che per avvisare l’utente, o nel linguaggio lato server.

Penso che sia una tecnica meno conosciuta tra i programmatori. Quindi, non c’è una grande accettazione per questo. E se hai un manager non tecnico per rivedere il tuo codice o rivedere il tuo lavoro, allora un’espressione regolare è pessima. Trascorrerai ore a scrivere un’espressione regolare perfetta, e otterrai pochi voti per il modulo pensando di aver scritto poche righe di codice. Inoltre, come detto altrove, leggere le espressioni regolari è un compito molto difficile.

Decenti sistemi di espressioni regolari come quelli usati in lex e yacc per la definizione del compilatore sono buoni, molto utili e puliti. In questi sistemi, i tipi di espressione sono definiti in termini di altri. Sono le orribili e illeggibili espressioni regolari di un solo liner, illeggibili, che si trovano comunemente nel codice perl e sed (ecc.) Che sono “controverse” (spazzatura).

Il miglior uso valido e normale per regex è per la convalida del formato dell’indirizzo e-mail.

Questa è una buona applicazione.

Ho usato espressioni regolari innumerevoli volte come una tantum in TextPad per massaggiare file flat, creare file CSV, creare istruzioni SQL insert e quel genere di cose.

Le espressioni regolari ben scritte non dovrebbero essere troppo lente. Di solito le alternative, come le tonnellate di chiamate a Sostituisci, sono opzioni molto più lente. Tanto vale farlo in un solo passaggio.

Molte situazioni richiedono espressioni esattamente regolari e nient’altro.

Sostituire caratteri speciali non stampabili con caratteri innocui è un altro buon uso.

Naturalmente posso immaginare che ci siano alcune codebase che sfruttano le espressioni regolari a scapito della mantenibilità. Non l’ho mai visto da solo. Sono stato effettivamente evitato dai revisori del codice per non aver usato abbastanza le espressioni regolari.