Qual è la differenza tra parentesi quadre e parentesi in una regex?


101

Ecco un'espressione regolare che ho creato per utilizzare in JavaScript:

var reg_num = /^(7|8|9)\d{9}$/

Eccone un altro suggerito dal mio membro del team.

var reg_num = /^[7|8|9][\d]{9}$/

La regola è convalidare un numero di telefono:

  • Dovrebbe essere di soli dieci numeri.
  • Il primo numero dovrebbe essere 7, 8 o 9.

Risposte:


124

Queste espressioni regolari sono equivalenti (per scopi di corrispondenza):

  • /^(7|8|9)\d{9}$/
  • /^[789]\d{9}$/
  • /^[7-9]\d{9}$/

La spiegazione:

  • (a|b|c)è una regex "OR" e significa "aob o c", sebbene la presenza di parentesi, necessarie per l'OR, catturi anche la cifra. Per essere strettamente equivalente, dovresti codificare (?:7|8|9)per renderlo un gruppo non di acquisizione.

  • [abc]è una "classe di caratteri" che significa "qualsiasi carattere di a, b o c" (una classe di caratteri può utilizzare intervalli, ad esempio [a-d]= [abcd])

Il motivo per cui queste espressioni regolari sono simili è che una classe di caratteri è una scorciatoia per un "o" (ma solo per singoli caratteri). In un'alternanza, puoi anche fare qualcosa di simile (abc|def)che non si traduce in una classe di caratteri.


30
(7|8|9)e [789]non sono equivalenti, perché il primo è catturare, il secondo no. (?:7|8|9)sarebbe equivalente d'altra parte (immagino che tu lo sappia ovviamente ...).
hochl

Sto vedendo questo regex: [<<|>>|\]\]|\[\[]. A causa del contesto, so che regex sta cercando di abbinare <<o >>o [[o ]]. Ma da quello che hai detto, dovrebbe corrispondere <o >o [o ]. Se usi |tra [], le parentesi si comportano diversamente?
Daniel Kaplan

1
@DanielKaplan non utilizzare |all'interno di una classe di caratteri [...], a meno che non si desideri abbinare il carattere pipe stesso. Anche la duplicazione di caratteri in una classe di caratteri non ha alcun effetto: una classe di caratteri è un elenco di caratteri e corrisponderà esattamente a uno di essi. La mia ipotesi è che tu voglia un gruppo , che usi le normali parentesi tonde:(<<|>>|\]\]|\[\[)
Bohémien

57

Il consiglio della tua squadra è quasi corretto, tranne per l'errore che è stato fatto. Una volta scoperto il motivo, non lo dimenticherai mai. Dai un'occhiata a questo errore.

/^(7|8|9)\d{9}$/

Cosa fa:

  • ^e $denota corrispondenze ancorate, che asserisce che il sottostruttura tra queste ancore è l'intera corrispondenza. La stringa corrisponderà solo se il subpattern corrisponde per intero, non solo a una sezione.
  • ()denota un gruppo di cattura .
  • 7|8|9denota che corrisponde ad una 7, 8o 9. Lo fa con le alternanze , che è ciò che fa l'operatore del tubo |, alternando le alternanze. Questo torna indietro tra le alternanze: se la prima alternanza non è abbinata, il motore deve tornare prima che la posizione del puntatore si sia spostata durante la corrispondenza dell'alternanza, per continuare ad abbinare l'alternanza successiva; Mentre la classe del personaggio può avanzare in sequenza. Guarda questa corrispondenza su un motore regex con ottimizzazioni disabilitate:
Pattern: (r|f)at
Match string: carat

alternanze

Pattern: [rf]at
Match string: carat

classe

  • \d{9}corrisponde a nove cifre. \dè un metacarattere shorthanded, che corrisponde a qualsiasi cifra.
/^[7|8|9][\d]{9}$/

Guarda cosa fa:

  • ^e $denota anche corrispondenze ancorate.
  • [7|8|9]è una classe di caratteri . Qualsiasi carattere dall'elenco 7, |, 8, |, o 9possono essere abbinate, così il |è stato aggiunto in modo non corretto. Ciò corrisponde senza tornare indietro.
  • [\d]è una classe di caratteri che abita il metacarattere \d. La combinazione dell'uso di una classe di caratteri e di un singolo metacarattere è una cattiva idea, a proposito, poiché lo strato di astrazione può rallentare la corrispondenza, ma questo è solo un dettaglio di implementazione e si applica solo ad alcune implementazioni di regex. JavaScript non è uno, ma allunga leggermente il modello secondario.
  • {9} indica che il singolo costrutto precedente viene ripetuto nove volte in totale.

L'espressione regolare ottimale è /^[789]\d{9}$/, poiché /^(7|8|9)\d{9}$/acquisisce inutilmente il che impone una diminuzione delle prestazioni sulla maggior parte delle implementazioni di espressioni regolari (sembra essere uno, considerando che la domanda utilizza la parola chiave varnel codice, questo probabilmente è JavaScript). L'impiego diche gira su PCRE per il preg matching ottimizzerà la mancanza di backtracking, tuttavia non siamo nemmeno in PHP, quindi usare le classi []invece delle alternanze |dà un bonus di prestazioni poiché la partita non torna indietro, e quindi entrambe le partite e falliscono più velocemente rispetto all'uso del tuo precedente espressione regolare.


6
solo per interesse, da quale programma proviene lo screenshot?
Mr Mystery Guest

12

I primi 2 esempi si comportano in modo molto diverso se li stai SOSTITUENDO con qualcosa. Se corrispondi a questo:

str = str.replace(/^(7|8|9)/ig,''); 

sostituiresti 7 o 8 o 9 con la stringa vuota.

Se corrispondi a questo

str = str.replace(/^[7|8|9]/ig,''); 

sostituirai 7o 8o 9O LA BARRA VERTICALE !!!! dalla stringa vuota.

L'ho appena scoperto nel modo più duro.


6
Benvenuto in SO! Sostituire o abbinare, è semplicemente sbagliato. Molte persone commettono questo errore e di solito la passano liscia - per anni, a volte - perché le loro stringhe di input non contengono mai una pipe ( |).
Alan Moore
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.