Perché la mia espressione regolare funziona in X ma non in Y?


77

Ho scritto un'espressione regolare che funziona bene in un determinato programma (grep, sed, awk, perl, python, ruby, ksh, bash, zsh, find, emacs, vi, vim, gedit, ...). Ma quando lo uso in un programma diverso (o su una diversa variante unix), smette di corrispondere. Perché?

Risposte:


103

Sfortunatamente, per motivi storici, strumenti diversi hanno una sintassi leggermente diversa dell'espressione regolare e talvolta alcune implementazioni hanno estensioni non supportate da altri strumenti. Sebbene ci sia un terreno comune, sembra che ogni autore di strumenti abbia fatto alcune scelte diverse.

La conseguenza è che se si dispone di un'espressione regolare che funziona in uno strumento, potrebbe essere necessario modificarlo per funzionare in un altro strumento. Le principali differenze tra gli strumenti comuni sono:

  • se gli operatori +?|(){}richiedono una barra rovesciata;
  • quali estensioni sono supportate oltre le basi .[]*^$e di solito+?|()

In questa risposta, elenco gli standard principali . Controlla la documentazione degli strumenti che stai utilizzando per i dettagli.

Il confronto di Wikipedia con i motori delle espressioni regolari ha una tabella che elenca le funzionalità supportate da implementazioni comuni.

Espressioni regolari di base (BRE)

Le espressioni regolari di base sono codificate dallo standard POSIX . È la sintassi utilizzata da grep, sede vi. Questa sintassi fornisce le seguenti funzionalità:

  • ^e $corrispondono solo all'inizio e alla fine di una riga.
  • . corrisponde a qualsiasi personaggio (o qualsiasi personaggio tranne una nuova riga).
  • […]corrisponde a qualsiasi carattere elencato tra parentesi (set di caratteri). Se il primo carattere dopo la parentesi quadra aperta è a ^, i caratteri che non sono elencati sono invece corrispondenti. Per includere a ], inseriscilo subito dopo l'apertura [(o dopo [^se si tratta di un set negativo). Se -è compreso tra due caratteri, indica un intervallo; per includere un valore letterale -, mettilo dove non può essere analizzato come intervallo.
  • Barra rovesciata prima di una qualsiasi delle ^$.*\[virgolette del carattere successivo.
  • * corrisponde al carattere precedente o alla sottoespressione 0, 1 o più volte.
  • \(…\)è un gruppo sintattico, da utilizzare con l' *operatore o backreferenze e \DIGITsostituzioni.
  • Backreference \1, \2, ... corrispondono al testo esatto corrispondente al gruppo corrispondente, ad esempio, \(fo*\)\(ba*\)\1le partite foobaafooma non foobaafo. Non esiste un modo standard per fare riferimento al decimo gruppo e oltre (il significato standard di \10è il primo gruppo seguito da a 0).

Le seguenti funzionalità sono anche standard, ma mancano in alcune implementazioni limitate:

  • \{m,n\}corrisponde al carattere o alla sottoespressione precedente tra m e n volte; n o m possono essere omessi e significa esattamente m .\{m\}
  • Tra parentesi, è possibile utilizzare le classi di caratteri , ad esempio [[:alpha:]]corrisponde a qualsiasi lettera. Le implementazioni moderne delle espressioni parentesi quadre includono anche elementi di confronto simili [.ll.]e classi di equivalenza simili [=a=].

Le seguenti sono estensioni comuni (specialmente negli strumenti GNU), ma non si trovano in tutte le implementazioni. Controlla il manuale dello strumento che stai utilizzando.

  • \|in alternanza: foo\|barpartite fooo bar.
  • \?(abbreviazione di \{0,1\}) e \+(abbreviazione di \{1,\}) corrispondono al carattere precedente o alla sottoespressione al massimo 1 volta o almeno 1 volta rispettivamente.
  • \ncorrisponde a una nuova riga, \tcorrisponde a una scheda, ecc.
  • \wcorrisponde a qualsiasi parola costituente (abbreviazione di [_[:alnum:]]ma con variazione quando si tratta di localizzazione) e \Wcorrisponde a qualsiasi carattere che non sia una parola costituente.
  • \<e \>abbina la stringa vuota solo all'inizio o alla fine di una parola rispettivamente; \bcorrisponde e \Bcorrisponde a dove \bno.

Si noti che gli strumenti senza l' \|operatore non hanno la piena potenza delle espressioni regolari. Le backreferenze consentono alcune cose extra che non possono essere fatte con espressioni regolari in senso matematico.

Espressioni regolari estese (ERE)

Le espressioni regolari estese sono codificate dallo standard POSIX . Il loro principale vantaggio rispetto a BRE è la regolarità: tutti gli operatori standard sono caratteri di punteggiatura nudi, una barra rovesciata prima che un carattere di punteggiatura lo citi sempre. È la sintassi usata da awk, grep -Eo egrep, GNU sed -r, e dall'operatore di bash=~ . Questa sintassi fornisce le seguenti funzionalità:

  • ^e $corrispondono solo all'inizio e alla fine di una riga.
  • . corrisponde a qualsiasi personaggio (o qualsiasi personaggio tranne una nuova riga).
  • […]corrisponde a qualsiasi carattere elencato tra parentesi (set di caratteri). Il completamento con un'iniziale ^e gli intervalli funzionano come in BRE (vedi sopra). Le classi di caratteri possono essere utilizzate ma mancano in alcune implementazioni. Le implementazioni moderne supportano anche classi di equivalenza ed elementi di confronto. Una barra rovesciata tra parentesi cita il carattere successivo in alcune ma non in tutte le implementazioni; usare \\per indicare una barra rovesciata per la portabilità.
  • (…)è un gruppo sintattico, da utilizzare *o \DIGITsostituire.
  • |in alternanza: foo|barpartite fooo bar.
  • *, +E ?corrisponde al carattere precedente o sotto-espressione di un certo numero di volte: 0 o più per *, 1 o più per +, 0 o 1 per ?.
  • La barra rovesciata cita il carattere successivo se non è alfanumerico.
  • {m,n}corrisponde al carattere precedente o sottoespressione tra m ed n volte (mancanti alcune implementazioni); n o m possono essere omessi e significa esattamente m .{m}
  • Alcune estensioni comuni come in BRE: backreferences (in particolare assenti in awk tranne nell'implementazione di busybox in cui è possibile utilizzare ); caratteri speciali , ecc .; confini di parole e , costituenti di parole e , ...\DIGIT$0 ~ "(...)\\1"\n\t\b\B\b\B

PCRE (espressioni regolari compatibili con Perl)

PCRE sono estensioni di ERE, originariamente introdotte da Perl e adottate da GNU grep -Pe da molti strumenti e linguaggi di programmazione moderni , generalmente tramite la libreria PCRE . Consulta la documentazione di Perl per una buona formattazione con esempi. PCRE non supporta tutte le funzionalità dell'ultima versione di Perl (ad es. L'esecuzione del codice Perl è supportata solo in Perl). Consultare il manuale PCRE per un riepilogo delle funzioni supportate. Le principali aggiunte a ERE sono:

  • (?:…)è un gruppo che non cattura: mi piace (…), ma non conta per i riferimenti indietro.
  • (?=FOO)BAR(lookahead) corrisponde BAR, ma solo se esiste anche una corrispondenza per FOOiniziare nella stessa posizione. Questo è molto utile per ancorare una corrispondenza senza includere il seguente testo nella corrispondenza: foo(?=bar)corrispondenze fooma solo se è seguita da bar.
  • (?!FOO)BAR(lookahead negativo) corrisponde BAR, ma non esiste anche una corrispondenza FOOnella stessa posizione. Ad esempio, (?!foo)[a-z]+corrisponde a qualsiasi parola minuscola che non inizia con foo; [a-z]+(?![0-9)corrisponde a qualsiasi parola minuscola che non è seguita da una cifra (quindi in foo123, corrisponde foma non foo).
  • (?<=FOO)BAR(lookbehind) corrisponde BAR, ma solo se è immediatamente preceduto da una corrispondenza per FOO. FOOdeve avere una lunghezza nota (non è possibile utilizzare operatori di ripetizione come *). Questo è molto utile per ancorare una corrispondenza senza includere il testo precedente nella corrispondenza: (?<=^| )foocorrispondenze, fooma solo se è preceduto da uno spazio o dall'inizio della stringa.
  • (?<!FOO)BAR(lookbehind negativo) corrisponde BAR, ma solo se non è immediatamente preceduto da una corrispondenza per FOO. FOOdeve avere una lunghezza nota (non è possibile utilizzare operatori di ripetizione come *). Questo è molto utile per ancorare una corrispondenza senza includere il testo precedente nella corrispondenza: (?<![a-z])foocorrispondenze fooma solo se non è preceduta da una lettera minuscola.

Emacs

La sintassi di Emacs è intermedia tra BRE ed ERE. Oltre a Emacs, è la sintassi predefinita per -regexin GNU find. Emacs offre i seguenti operatori:

  • ^, $, ., […], *, +, ?Come in ERE
  • \(…\), \|, \{…\}, Come in BRE\DIGIT
  • più sequenze di lettere rovesciate ; \<e \>per i confini delle parole; e altro nelle ultime versioni di Emacs, che spesso non sono supportate in altri motori con una sintassi simile a Emacs.

Globs Shell

I globs di shell (caratteri jolly) eseguono la corrispondenza dei motivi con una sintassi completamente diversa dalle espressioni regolari e meno potente. Oltre alle shell, questi caratteri jolly sono disponibili con altri strumenti come i find -namefiltri e rsync. I modelli POSIX includono le seguenti funzionalità:

  • ? corrisponde a qualsiasi singolo personaggio.
  • […]è un set di caratteri come nelle comuni sintassi delle espressioni regolari. Alcune shell non supportano le classi di caratteri. Alcune shell richiedono !invece di ^negare il set.
  • *corrisponde a qualsiasi sequenza di caratteri (spesso tranne /quando si corrispondono percorsi di file; se /è escluso *, a **volte include /, ma controllare la documentazione dello strumento).
  • La barra rovesciata cita il carattere successivo.

Ksh offre funzionalità aggiuntive che conferiscono al suo modello la piena potenza delle espressioni regolari. Queste funzionalità sono disponibili anche in bash dopo l'esecuzione shopt -s extglob. Zsh ha una sintassi diversa ma può anche supportare la sintassi di ksh dopo setopt ksh_glob.


Altri ricchi RE che potresti voler menzionare sono quelli vimlibast AT & T (come in ksh93).
Stéphane Chazelas,

@ StéphaneChazelas Oltre a vim, quale programma utilizza vim regexps? Oltre a ksh, quale programma usa libast?
Gilles 'SO- smetti di essere malvagio' il

tutto il set di strumenti di AT & T utilizza le ER AT & T ( grep, tw, expr...). Tranne kshche, quel set di strumenti si trova raramente al di fuori di AT&T.
Stéphane Chazelas,

Secondo la mia comprensione (e Wikipedia), il tuo termine "Classe di caratteri" in realtà si riferisce a "Classe di caratteri POSIX" ... tuttavia, è d' regex(7)accordo con te e chiama [these]"espressioni di parentesi" e (all'interno di "espressioni di parentesi") [:these:]"classi di caratteri". Non sono sicuro di come affrontarlo al meglio.
Adam Katz,

Qualunque cosa tu li chiami, supportano le gamme. Vale sicuramente la pena notare che -specifica un intervallo e dovrebbe essere evitato, prima (dopo il facoltativo ^), o infine se deve essere preso alla lettera. (Ho visto un sacco di bug derivanti da, ad esempio, [A-z]annotare la modifica nel caso, che corrisponde ai caratteri dei codici da 65 a 122 e include accidentalmente ciascuno di:. [\]^_`Ho anche visto il valido ma confuso [!-~]per abbinare tutti i caratteri stampabili in ANSI , che preferisco vedere come [\x21-\x7e], che è almeno semplice nella sua azione sebbene confuso in una dimensione diversa.)
Adam Katz
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.