La flag unicode /.../u
abilita il corretto supporto delle coppie surrogate.
Qui vi sono i valori unicode da comparare:
Carattere | Unicode | Bytes |
---|---|---|
a |
0x0061 | 2 |
≈ |
0x2248 | 2 |
𝒳 |
0x1d4b3 | 4 |
𝒴 |
0x1d4b4 | 4 |
😄 |
0x1f604 | 4 |
Dunque caratteri come a
e ≈
occupano 2 bytes, e quelli rari ne occupano 4.
Unicode è stato fatto in modo tale che i caratteri a 4 byte abbiano un significato solo considerando l’intero insieme.
In precedenza JavaScript non ne sapeva nulla, e molti metodi delle stringhe ancora presentano problemi. Per esempio, length
pensa che qui ci siano due caratteri:
alert('😄'.length); // 2
alert('𝒳'.length); // 2
…Ma possiamo vedere che ce n’è solo uno, giusto? Il punto è che length
tratta i caratteri a 4 byte come due caratteri a 2-byte. Questo non è corretto, perché devono essere considerati solo insieme (per cui chiamati “coppie surrogate”).
Usualmente, anche le espressioni regolari trattano questi “caratteri lunghi” come due caratteri a 2-byte.
Questo porta a strani risultati, ad esempio proviamo a cercare [𝒳𝒴]
nella stringa 𝒳
:
alert( '𝒳'.match(/[𝒳𝒴]/) ); // risultato strano (in realtà è una corrispondenza errata, "mezzo carattere")
Il risultato è errato, perché di default il motore delle regexp non comprende le coppie surrogate.
Dunque, pensa che [𝒳𝒴]
non siano due, ma quattro caratteri:
- la metà sinistra di
𝒳
(1)
, - la metà destra di
𝒳
(2)
, - la metà sinistra di
𝒴
(3)
, - la metà destra di
𝒴
(4)
.
Li possiamo elencare così:
for(let i=0; i<'𝒳𝒴'.length; i++) {
alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500
};
Quindi trova solo la “metà sinistra” di 𝒳
.
In altre parole, la ricerca funziona come '12'.match(/[1234]/)
: solo 1
viene restituito.
La flag “u”
La flag /.../u
risolve questo problema.
Essa abilita le coppie surrogate nel motore delle regexp, in modo tale che il risultato sia:
alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳
Vediamo un altro esempio.
Se dimentichiamo la flag u
e occasionalmente usiamo le coppie surrogate, possiamo incorrere in errori:
'𝒳'.match(/[𝒳-𝒴]/); // SyntaxError: intervallo non valido nella classe di caratteri
Di solito, le regexp interpretano [a-z]
come un "intervallo di caratteri con codici tra a
e z
.
Ma senza la flag u
, le coppie surrogate vengono interpretate come “coppie di caratteri indipendenti”, quindi [𝒳-𝒴]
è come [<55349><56499>-<55349><56500>]
(sostituito a ogni coppia surrogata il codice corrispondente). Ora possiamo vedere con più chiarezza che l’intervallo 56499-55349
non è accettabile, dato che il valore a sinistra dell’intervallo deve essere inferiore rispetto a quello a destra.
Usando la flag u
tutto funziona di nuovo:
alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴