Domanda

Qualsiasi codice che ho visto che utilizza Regexes tende a usarli come una scatola nera:

  1. Metti in stringa
  2. Magic Regex
  3. Esci dalla stringa

Questa non sembra una buona idea da usare nel codice di produzione, poiché anche una piccola modifica può spesso comportare una regex completamente diversa.

A parte i casi in cui lo standard è permanente e immutabile, le regex sono il modo di fare le cose o è meglio provare metodi diversi?

È stato utile?

Soluzione

Se le regex sono lunghe e impenetrabili, rendendole difficili da mantenere, dovrebbero essere commentate.

Molte implementazioni di regex ti permettono di riempire le regex con spazi bianchi e commenti.
Vedi http://www.regular-expressions.info/comments.html
e horror in codice: Espressioni regolari: ora hai due problemi

  

Qualsiasi codice che ho visto che utilizza Regexes tende a usarli come una scatola nera:

Se per scatola nera intendi l'astrazione, questo è tutto ciò che la programmazione è, cercando di sottrarre la parte difficile (analizzare le stringhe) in modo che tu possa concentrarti sul dominio del problema (che tipo di stringhe voglio abbinare).

  

anche una piccola modifica può spesso comportare una regex completamente diversa.

Questo è vero per qualsiasi codice. Finché stai testando la tua regex per assicurarti che corrisponda alle stringhe che ti aspetti, idealmente con unit test , dovresti essere sicuro di cambiarle.

Modifica: leggi anche il commento di Jeff a questa risposta sul codice di produzione.

Altri suggerimenti

Obbligatorio.

Dipende davvero dalla regex. Se è questa enorme espressione monolitica, allora sì, è un problema di manutenibilità. Se riesci a esprimerli in modo succinto (forse spezzandoli) o se hai buoni commenti e strumenti per aiutarti a capirli, allora possono essere uno strumento potente.

Non so quale lingua stai usando, ma Perl - ad esempio - supporta il flag x, quindi gli spazi vengono ignorati nelle regex a meno che non siano salvati, quindi puoi dividerlo in più righe e commentare tutto in linea:

$foo =~ m{
    (some-thing)          # matches something
    \s*                   # matches any amount of spaces
    (match another thing) # matches something else
}x;

Questo aiuta a rendere più leggibili le regex lunghe.

Sembra magico solo se non capisci la regex. Qualsiasi numero di piccole modifiche al codice di produzione può causare gravi problemi, quindi non è una buona ragione, secondo me, per non usare regex. Test approfonditi dovrebbero evidenziare eventuali problemi.

Piccole modifiche a qualsiasi codice in qualsiasi lingua possono portare a completamente risultati diversi. Alcuni addirittura impediscono la compilazione.

Sostituisci regex con " C " oppure " C # " oppure " Java " oppure " Python " oppure " Perl " oppure " SQL " oppure " Ruby " oppure " awk " o ... qualsiasi cosa, davvero, e ottieni la stessa domanda.

Regex è solo un'altra lingua, Huffman codificato per essere efficiente nella corrispondenza delle stringhe. Proprio come Java, Perl, PHP o soprattutto SQL, ogni lingua ha punti di forza e di debolezza e devi conoscere la lingua in cui stai scrivendo quando la scrivi (o mantenerla) per avere qualche speranza di essere produttivo.

Modifica: Mike, i regex sono codificati da Huffman in quanto le cose comuni da fare sono più brevi di quelle più rare. Le corrispondenze letterali di testo sono generalmente un singolo carattere (quello che si desidera abbinare). Esistono caratteri speciali: quelli comuni sono brevi. Costrutti speciali, come (? :) sono più lunghi. Queste non sono le stesse cose che sarebbero comuni nei linguaggi di uso generale come Perl, C ++, ecc., Quindi il codice Huffman era mirato a questa specializzazione.

Le regex complesse sono fuoco e dimentica per me. Scrivilo, testalo e quando funziona, scrivi un commento su cosa fa e stiamo bene.

In molti casi, tuttavia, è possibile suddividere le espressioni regolari in parti più piccole, magari scrivere del codice ben documentato che combina queste regex. Ma se trovi una regex multilinea nel tuo codice, è meglio che tu non sia colui che deve mantenerlo :)

Sembra familiare? Questo è più o meno vero per qualsiasi codice. Non vuoi avere metodi molto lunghi, non vuoi avere classi molto lunghe e non vuoi avere espressioni regolari molto lunghe, sebbene i metodi e le classi siano di gran lunga più facili da refactoring. Ma in sostanza, è lo stesso concetto.

I Regex non sono l'unico modo per fare qualcosa. Puoi fare logicamente nel codice tutto ciò che può fare un'espressione regolare. Le espressioni regolari sono solo

  1. veloce
  2. Testato e provato
  3. Potente

RegExs può essere molto gestibile, se si utilizzano nuove funzionalità introdotte da Perl 5.10. Le funzioni a cui mi riferisco sono funzionalità di back port da Perl 6.

Esempio copiato direttamente da perlretut .

Definizione di schemi denominati

Alcune espressioni regolari usano schemi identici in diversi punti. A partire da Perl 5.10, è possibile definire sottotatri denominati in una sezione del modello in modo che possano essere richiamati per nome in qualsiasi punto del modello. Questo modello sintattico per questo gruppo di definizioni è (?(DEFINE)(?<name>pattern)...). Un inserimento di un modello denominato è scritto come (?&name).

L'esempio seguente illustra questa funzione usando il modello per i numeri in virgola mobile presentato in precedenza. I tre modelli secondari utilizzati più di una volta sono il segno facoltativo, la sequenza di cifre per un numero intero e la frazione decimale. Il gruppo DEFINE alla fine del modello contiene la sua definizione. Si noti che il modello di frazione decimale è il primo posto in cui è possibile riutilizzare il modello intero.

/^
  (?&osg)\ * ( (?&int)(?&dec)? | (?&dec) )
        (?: [eE](?&osg)(?&int) )?
 $
 (?(DEFINE)
     (?<osg>[-+]?)         # optional sign
     (?<int>\d++)          # integer
     (?<dec>\.(?&int))     # decimal fraction
 )
/x

Se utilizzate in modo consapevole, le espressioni regolari sono un potente meccanismo che ti risparmia da righe e righe di possibili analisi del testo. Dovrebbero ovviamente essere documentati in modo corretto ed efficiente al fine di verificare se i presupposti iniziali sono ancora validi e altrimenti aggiornati di conseguenza. Per quanto riguarda la manutenzione, IMHO è meglio cambiare una singola riga di codice (il modello di espressione regolare) piuttosto che comprendere le righe e le righe del codice di analisi o qualunque sia lo scopo delle espressioni regolari.

Le regex sono il modo di fare le cose? Dipende dall'attività.

Come per tutto ciò che riguarda la programmazione, non esiste una risposta giusta e sbagliata.

Se un regexp risolve un determinato compito in modo rapido e semplice, allora è probabilmente meglio di una soluzione più dettagliata.

Se una regexp sta cercando di realizzare un compito complicato, allora qualcosa di più dettagliato potrebbe essere più semplice da capire e quindi mantenere.

famosa citazione sulle regex:

  

" Alcune persone, quando affrontano un problema, pensano   & # 8220; lo so, userò espressioni regolari. & # 8221; Ora hanno due problemi. & Quot; - Jamie Zawinski

Quando uso regex, trovo che siano mantenibili, ma sono usati in casi speciali. Di solito esiste un metodo migliore, non regex, per fare quasi tutto.

Esistono molte possibilità per rendere RegEx più gestibile. Alla fine è solo una tecnica che un programmatore (bravo?) Deve imparare quando si tratta di cambiamenti importanti (o talvolta anche minori). Quando non ci fossero dei veri professionisti, nessuno si preoccuperebbe di loro a causa della loro sintassi complessa. Ma sono veloci, compatti e molto flessibili nello svolgere il loro lavoro.

Per .NET People potrebbe esserci il " Linq a RegEx " libreria peggio un'occhiata o " Libreria di espressioni regolari leggibili " ;. Li rende più facili da mantenere e ancora più facili da scrivere. Li ho usati entrambi nei propri progetti, sapevo che il codice sorgente HTML che ho analizzato con loro poteva cambiare in qualsiasi momento.

Ma fidati di me: quando ti addentri con loro, potrebbero persino divertirsi a scrivere e leggere. :)

Ho una politica di commentare a fondo regex non banali. Ciò significa descrivere e giustificare ogni atomo che non corrisponde a se stesso. Alcune lingue (Python, per esempio) offrono & Quot; verbose & Quot; regex che ignorano gli spazi bianchi e consentono i commenti; usalo quando possibile. Altrimenti, vai atomo per atomo in un commento sopra la regex.

Il problema non è con le stesse regex, ma piuttosto con il loro trattamento come una scatola nera. Come con qualsiasi linguaggio di programmazione, la manutenibilità ha più a che fare con la persona che l'ha scritta e la persona che la legge piuttosto che con la lingua stessa.

C'è anche molto da dire sull'uso dello strumento giusto per il lavoro. Nell'esempio che hai citato nel tuo commento al post originale, una regex è lo strumento sbagliato da utilizzare per l'analisi dell'HTML, come è menzionato piuttosto frequentemente su PerlMonks. Se provi ad analizzare l'HTML in qualcosa che assomigli a un modo generale usando solo una regex, finirai per farlo in un modo errato e fragile, scrivendo una mostruosità orrenda e irraggiungibile di una regex, o (molto probabilmente) entrambe le cose.

La tua domanda non & # 8217; t sembra riguardare le espressioni regolari stesse, ma solo la sintassi generalmente usata per esprimere espressioni regolari. Tra molti programmatori hardcore, questa sintassi è stata accettata come piuttosto succinta e potente, ma per espressioni regolari più lunghe è in realtà davvero illeggibile e non mantenibile.

Alcune persone hanno già menzionato & # 8220; x & # 8221; flag in Perl, che aiuta un po ', ma non molto.

Mi piacciono molto le espressioni regolari, ma non la sintassi. Sarebbe bello poter costruire un'espressione regolare da nomi di metodi leggibili e significativi. Ad esempio, invece di questo codice C #:

foreach (var match in Regex.Matches(input, @"-?(?<number>\d+)"))
{
    Console.WriteLine(match.Groups["number"].Value);
}

potresti avere qualcosa di molto più dettagliato ma molto più leggibile e mantenibile:

int number = 0;
Regex r = Regex.Char('-').Optional().Then(
    Regex.Digit().OneOrMore().Capture(c => number = int.Parse(c))
);
foreach (var match in r.Matches(input))
{
    Console.WriteLine(number);
}

Questa è solo un'idea rapida; So che ci sono altri problemi di manutenibilità non correlati con questo (anche se direi che sono sempre più piccoli). Un ulteriore vantaggio di ciò è la verifica in fase di compilazione.

Naturalmente, se pensi che questo sia esagerato e troppo , puoi comunque avere una sintassi delle espressioni regolari che si trovi da qualche parte nel mezzo, forse ...

instead of:   -?(?<number>\d+)
could have:   ("-" or "") + (number = digit * [1..])

Questo è ancora un milione di volte più leggibile e solo il doppio del tempo. Tale sintassi può essere facilmente fatta per avere la stessa potenza espressiva delle normali espressioni regolari e può certamente essere integrata in un compilatore di un linguaggio di programmazione per analisi statiche.

I don & # 8217; so davvero perché c'è così tanta opposizione a ripensare la sintassi per le espressioni regolari anche quando vengono ripensati interi linguaggi di programmazione (ad esempio Perl 6 o quando C # era nuovo). Inoltre, l'idea molto dettagliata di cui sopra non è nemmeno incompatibile con & # 8220; vecchio & # 8221; espressioni regolari; l'API potrebbe essere facilmente implementata come quella che costruisce un'espressione regolare vecchio stile sotto il cofano.

Regex è stato indicato come " scrivi solo " linguaggio di programmazione di sicuro. Tuttavia, non penso che ciò significhi che dovresti evitarli. Penso solo che dovresti commentare l'inferno per il loro intento. Di solito non sono un grande fan di commenti che spiegano cosa fa una riga, posso leggere il codice per quello, ma i Regex sono l'eccezione. Commenta tutto!

Di solito vado nella misura in cui scrivo un file delle specifiche dello scanner. Uno scanner o & Quot; generatore scanner & Quot; è essenzialmente un parser di testo ottimizzato. Dato che di solito lavoro con Java, il mio metodo preferito è JFlex ( http://www.jflex.de ), ma c'è anche Lex, YACC e molti altri.

Gli scanner funzionano su espressioni regolari che è possibile definire come macro. Quindi si implementano i callback quando le espressioni regolari corrispondono a una parte del testo.

Quando si tratta del codice ho un file di specifica contenente tutta la logica di analisi. Lo eseguo attraverso lo strumento generatore di scanner preferito per generare il codice sorgente nella lingua prescelta. Quindi avvolgo tutto ciò in una funzione di analisi o in una classe di qualche tipo. Questa astrazione semplifica quindi la gestione di tutta la logica delle espressioni regolari ed è un'ottima prestazione. Certo, è eccessivo se stai lavorando con solo una o due regexps, e ci vogliono facilmente almeno 2-3 giorni per imparare cosa diavolo sta succedendo, ma se mai lavori con, diciamo, 5 o 6 o 30 di questi, diventa una funzionalità davvero interessante e l'implementazione della logica di analisi inizia a richiedere solo pochi minuti e rimangono facili da mantenere e facili da documentare.

Ho sempre affrontato questo problema come un problema di blocco predefinito.

Non devi solo scrivere una regex di 3000 caratteri e sperare per il meglio. Scrivi un mucchio di piccoli pezzi che aggiungi insieme.

Ad esempio, per abbinare un URI, hai protocollo, autorità, sottodominio, dominio, tld, percorso, argomenti (almeno). E alcuni di questi sono opzionali!

Sono sicuro che potresti scrivere un mostro per gestirlo, ma è più facile scrivere blocchi e aggiungerli insieme.

Di solito divido la regex in pezzi con commenti, quindi li metto tutti insieme per la spinta finale. I pezzi possono essere sottostringhe o elementi di matrice

Due esempi di PHP PCRE (i dettagli o l'uso particolare non sono importanti):

1)
  $dktpat = '/^[^a-z0-9]*'. // skip any initial non-digits
    '([a-z0-9]:)?'. // division within the district
    '(\d+)'. // year
    '((-)|-?([a-z][a-z])-?)'. // type of court if any - cv, bk, etc.
    '(\d+)'. // docket sequence number
    '[^0-9]*$/i'; // ignore anything after the sequence number
  if (preg_match($dktpat,$DocketID,$m)) {

2)
    $pat= array (
      'Row'        => '\s*(\d*)',
      'Parties'    => '(.*)',
      'CourtID'    => '<a[^>]*>([a-z]*)</a>',
      'CaseNo'     => '<a[^>]*>([a-z0-9:\-]*)</a>',
      'FirstFiled' => '([0-9\/]*)',
      'NOS'        => '(\d*)',
      'CaseClosed' => '([0-9\/]*)',
      'CaseTitle'  => '(.*)',
    );
    // wrap terms in table syntax
    $pat = '#<tr>(<td[^>]*>'.
      implode('</td>)(</tr><tr>)?(<td[^>]*>',$pat).
      '</td>)</tr>#iUx';
    if (preg_match_all ($pat,$this->DocketText,$matches, PREG_PATTERN_ORDER))

Li uso nelle mie app ma mantengo l'espressione regEx effettiva nel file di configurazione, quindi se il testo sorgente che sto analizzando (ad esempio una e-mail) cambia formato per qualche motivo, posso aggiornare rapidamente la configurazione per gestire la modifica senza ricostruire l'app.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top