Alcuni caratteri o classi di caratteri inseriti all’interno di parantesi quadre […] significano “cerca qualsiasi carattere tra quelli forniti”.
Insiemi
Per esempio, [eao] significa uno qualunque dei 3 caratteri: 'a', 'e', od 'o'.
Questo è chiamato un insieme o set. I set posso essere usati in una regexp insieme ad altri caratteri:
// trova [t o m], e quindi "op"
alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top"
Si noti che sebbene ci siano più caratteri nel set, questi corrispondano esattamente a un carattere nel match.
Quindi il seguente esempio non dà alcuna corrispondenza:
// trova "V", poi ['o' o 'i'], quindi "la"
alert( "Voila".match(/V[oi]la/) ); // null, nessuna corrispondenza
Il modello di ricerca risulta quindi:
V,- poi una di queste lettere
[oi], - quindi
la.
Significa che ci dovrebbe essere una corrispondenza per Vola o Vila.
Intervalli
Le parentesi quadre possono contenere anche intervalli di caratteri.
Per esempio, [a-z] indica un carattere nell’intervallo che va da a a z, e [0-5] indica un numero tra 0 e 5.
Nell’esempio seguente cercheremo una "x" seguita da due numeri o lettere da A a F:
alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF
Il modello [0-9A-F] ha due intervalli: cerca un carattere che sia una cifra da 0 a 9 o una lettera da A a F.
Se volessimo cercare anche lettere minuscole, possiamo aggiungere l’intervallo a-f: [0-9A-Fa-f], o aggiungere il flag i.
Possiamo anche usare classi di caratteri dentro […].
Per esempio, se volessimo cercare un carattere di parola \w o un trattino -, allora l’insieme sarà [\w-].
È anche possibile combinare diverse classi, es [\s\d] significa “uno spazio o un numero”.
Per esempio:
- \d – è la stessa cosa di
[0-9], - \w – è la stessa cosa di
[a-zA-Z0-9_], - \s – è la stessa cosa di
[\t\n\v\f\r ]e pochi altri rari caratteri Unicode.
Esempio: multi lingua \w
Dal momento che la classe di caratteri \w è una scorciatoia per [a-zA-Z0-9_], non può trovare geroglifici cinesi, lettere cirilliche, ecc.
Possiamo allora scrivere un modello più universale, che cerca un carattere di parola in qualunque lingua. Questo è reso facile dalle proprietà Unicode: [\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}].
Decifriamolo. Similarmente a \w, stiamo creando un nostro insieme che include i caratteri con le seguenti proprietà Unicode:
Alphabetic(Alpha) – per le lettere,Mark(M) – per gli accenti,Decimal_Number(Nd) – per i numeri,Connector_Punctuation(Pc) – per il trattino basso'_'e caratteri simili,Join_Control(Join_C) – due codici speciali200ce200d, usati nelle legature, a.e. in Arabo.
Un esempio di utilizzo:
let regexp = /[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]/gu;
let str = `Hi 你好 12`;
// Trova tutte le lettere e i numeri:
alert( str.match(regexp) ); // H,i,你,好,1,2
Naturalmente possiamo modificare questo modello: aggiungere proprietà Unicode o rimuoverle. Le proprietà Unicode sono descritte meglio nell’articolo Unicode: flag "u".
Le proprietà Unicode p{…} non sono implementate in IE. Se ne abbiamo davvero bisogno possiamo utilizzare la libreria XRegExp.
In alternativa possiamo utilizzare soltanto un intervallo di caratteri nella lingua che ci interessa, a.e. [а-я] per le lettere cirilliche.
Esclusione di intervalli
Oltre ai normali intervalli, è possibile creare dei modelli di “esclusione”, come [^…].
Sono contraddistinti da un accento circonflesso ^ all’inizio e trovano corrispondenza in qualunque carattere tranne quelli indicati.
Per esempio:
[^aeyo]– qualunque carattere tranne'a','e','y'o'o'.[^0-9]– qualunque carattere tranne un numero, come\D.[^\s]– qualunque carattere che non sia uno spazio, come\S.
L’esempio seguente cerca qualunque carattere eccetto lettere, numeri e spazi:
alert( "alice15@gmail.com".match(/[^\d\sA-Z]/gi) ); // @ e .
L’escape dentro […]
In genere quando vogliamo trovare esattamente un carattere speciale, dobbiamo effettuarne l’escape: \.. Se abbiamo bisogno di un backslash, allora dobbiamo usare \\, e così via.
Dentro le parentesi quadre, possiamo usare la stragrande maggioranza di caratteri speciali senza la necessità di effettuarne l’escape:
- I simboli
. + ( )non necessitano mai di escaping. - Il trattino
-non è preceduto da caratteri di escape all’inizio o alla fine (dove non definisce un intervallo). - Un accento circonflesso
^è soggetto ad escape solo all’inizio (dove significa esclusione). - La parentesi quadra di chiusura
]dev’essere sempre soggetta ad escape (se abbiamo bisogno di cercare questo simbolo).
In altre parole, tutti i caratteri speciali sono consentiti senza necessità di escape, eccetto quando significano qualcosa all’interno delle parentesi quadre.
Un punto . all’interno delle parentesi quadre significa soltanto un punto. Il modello [.,] cercherebbe uno dei caratteri: o un punto o una virgola.
Nell’esempio seguente la regexp [-().^+] effettua la ricerca per uno dei caratteri -().^+:
// Non necessita di escape
let regexp = /[-().^+]/g;
alert( "1 + 2 - 3".match(regexp) ); // Corrispondono +, -
…Ma se decidete di effettuare l’escape “per ogni evenienza”, il risultato non cambierebbe:
// Escape di ogni carattere
let regexp = /[\-\(\)\.\^\+]/g;
alert( "1 + 2 - 3".match(regexp) ); // funziona ugualmente: +, -
Intervalli e flag “u”
Se ci sono coppie surrogate nel set, il flag u è necessario affinché la ricerca funzioni correttamente.
Per esempio, cerchiamo [𝒳𝒴] nella stringa 𝒳:
alert( '𝒳'.match(/[𝒳𝒴]/) ); // mostra uno strano carattere, come [?]
// (la ricerca è stata eseguita in modo errato, viene restituito mezzo-carattere)
Il risultato non è corretto, perché di base le espressioni regolari “non sanno nulla” riguardo le coppie surrogate.
Il motore delle espressioni regolari pensa che [𝒳𝒴] – non sono due, ma quattro caratteri:
- metà alla sinistra di
𝒳(1), - metà alla destra di
𝒳(2), - metà alla sinistra di
𝒴(3), - metà alla destra di
𝒴(4).
Possiamo vedere il suo codice in questo modo:
for(let i=0; i<'𝒳𝒴'.length; i++) {
alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500
};
Quini, l’esempio qui sopra trova e visualizza la metà alla sinistra di 𝒳.
Se aggiungiamo il flag u, allora il comportamento sarà corretto:
alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳
Una situazione simile si verifica quando si cerca un intervallo, come [𝒳-𝒴].
Se dimentichiamo di aggiungere il flag u, ci sarà un errore:
'𝒳'.match(/[𝒳-𝒴]/); // Errore: Invalid regular expression
La ragione è che senza il flag u le coppie surrogate sono percepite come due caratteri, quindi [𝒳-𝒴] è interpretato come [<55349><56499>-<55349><56500>] (ogni coppia surrogata è sostituita con i suoi codici). Ora è facile osservare che l’intervallo 56499-55349 non è valido: il suo codice iniziale 56499 è maggiore di quello finale 55349. Questa è la ragione formale dell’errore.
Con il flag u il modello funziona correttamente:
// cerca i caratteri da 𝒳 a 𝒵
alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴
Commenti
<code>, per molte righe – includile nel tag<pre>, per più di 10 righe – utilizza una sandbox (plnkr, jsbin, codepen…)