Varios caracteres o clases de caracteres entre corchetes […]
significa “buscar cualquier carácter entre los dados”.
Conjuntos
Por ejemplo, [eao]
significa cualquiera de los 3 caracteres: 'a'
, 'e'
, o 'o'
.
A esto se le llama conjunto. Los conjuntos se pueden usar en una expresión regular junto con los caracteres normales:
// encontrar [t ó m], y luego "op"
alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top"
Tenga en cuenta que aunque hay varios caracteres en el conjunto, corresponden exactamente a un carácter en la coincidencia.
Entonces, en el siguiente ejemplo no hay coincidencias:
// encuentra "V", luego [o ó i], luego "la"
alert( "Voila".match(/V[oi]la/) ); // null, sin coincidencias
El patrón busca:
V
,- después una de las letras
[oi]
, - después
la
.
Entonces habría una coincidencia para Vola
o Vila
.
Rangos
Los corchetes también pueden contener rangos de caracteres.
Por ejemplo, [a-z]
es un carácter en el rango de a
a z
, y [0-5]
es un dígito de 0
a 5
.
En el ejemplo a continuación, estamos buscando "x"
seguido de dos dígitos o letras de A
a F
:
alert( "Excepción 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF
Aquí [0-9A-F]
tiene dos rangos: busca un carácter que sea un dígito de 0
a 9
o una letra de A
a F
.
Si también queremos buscar letras minúsculas, podemos agregar el rango a-f
: [0-9A-Fa-f]
. O se puede agregar la bandera i
.
También podemos usar clases de caracteres dentro de los […]
.
Por ejemplo, si quisiéramos buscar un carácter de palabra \w
o un guion -
, entonces el conjunto es [\w-]
.
También es posible combinar varias clases, p.ej.: [\s\d]
significa “un carácter de espacio o un dígito”.
Por ejemplo:
- \d – es lo mismo que
[0-9]
, - \w – es lo mismo que
[a-zA-Z0-9_]
, - \s – es lo mismo que
[\t\n\v\f\r ]
, además de otros caracteres de espacio raros de unicode.
Ejemplo: multi-idioma \w
Como la clase de caracteres \w
es una abreviatura de [a-zA-Z0-9_]
, no puede coincidir con sinogramas chinos, letras cirílicas, etc.
Podemos escribir un patrón más universal, que busque caracteres de palabra en cualquier idioma. Eso es fácil con las propiedades unicode: [\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]
.
Decifrémoslo. Similar a \w
, estamos creando un conjunto propio que incluye caracteres con las siguientes propiedades unicode:
Alfabético
(Alpha
) – para letras,Marca
(M
) – para acentos,Numero_Decimal
(Nd
) – para dígitos,Conector_Puntuación
(Pc
) – para guion bajo'_'
y caracteres similares,Control_Unión
(Join_C
) – dos códigos especiales200c
and200d
, utilizado en ligaduras, p.ej. en árabe.
Un ejemplo de uso:
let regexp = /[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]/gu;
let str = `Hola 你好 12`;
// encuentra todas las letras y dígitos:
alert( str.match(regexp) ); // H,o,l,a,你,好,1,2
Por supuesto, podemos editar este patrón: agregar propiedades unicode o eliminarlas. Las propiedades Unicode se cubren con más detalle en el artículo Unicode: bandera "u" y clase \p{...}.
Las propiedades Unicode p{…}
no se implementaron en IE. Si realmente las necesitamos, podemos usar la biblioteca XRegExp.
O simplemente usa rangos de caracteres en el idioma de tu interés, p.ej. [а-я]
para letras cirílicas.
Excluyendo rangos
Además de los rangos normales, hay rangos “excluyentes” que se parecen a [^…]
.
Están denotados por un carácter caret ^
al inicio y coinciden con cualquier carácter excepto los dados.
Por ejemplo:
[^aeyo]
– cualquier carácter excepto'a'
,'e'
,'y'
u'o'
.[^0-9]
– cualquier carácter excepto un dígito, igual que\D
.[^\s]
– cualquiere carácter sin espacio, igual que\S
.
El siguiente ejemplo busca cualquier carácter, excepto letras, dígitos y espacios:
alert( "alice15@gmail.com".match(/[^\d\sA-Z]/gi) ); // @ y .
Escapando dentro de corchetes […]
Por lo general, cuando queremos encontrar exactamente un carácter especial, necesitamos escaparlo con \.
. Y si necesitamos una barra invertida, entonces usamos \\
, y así sucesivamente.
Entre corchetes podemos usar la gran mayoría de caracteres especiales sin escaparlos:
- Los símbolos
. + ( )
nunca necesitan escape. - Un guion
-
no se escapa al principio ni al final (donde no define un rango). - Un carácter caret
^
solo se escapa al principio (donde significa exclusión). - El corchete de cierre
]
siempre se escapa (si se necesita buscarlo).
En otras palabras, todos los caracteres especiales están permitidos sin escapar, excepto cuando significan algo entre corchetes.
Un punto .
dentro de corchetes significa solo un punto. El patrón [.,]
Buscaría uno de los caracteres: un punto o una coma.
En el siguiente ejemplo, la expresión regular [-().^+]
busca uno de los caracteres -().^+
:
// no es necesario escaparlos
let regexp = /[-().^+]/g;
alert( "1 + 2 - 3".match(regexp) ); // Coincide +, -
…Pero si decides escaparlos “por si acaso”, no habría daño:
// Todo escapado
let regexp = /[\-\(\)\.\^\+]/g;
alert( "1 + 2 - 3".match(regexp) ); // funciona también: +, -
Rangos y la bandera (flag) “u”
Si hay pares sustitutos en el conjunto, se requiere la flag u
para que funcionen correctamente.
Por ejemplo, busquemos [𝒳𝒴]
en la cadena 𝒳
:
alert( '𝒳'.match(/[𝒳𝒴]/) ); // muestra un carácter extraño, como [?]
// (la búsqueda se realizó incorrectamente, se devolvió medio carácter)
El resultado es incorrecto porque, por defecto, las expresiones regulares “no saben” sobre pares sustitutos.
El motor de expresión regular piensa que la cadena [𝒳𝒴]
no son dos, sino cuatro caracteres:
- mitad izquierda de
𝒳
(1)
, - mitad derecha de
𝒳
(2)
, - mitad izquierda de
𝒴
(3)
, - mitad derecha de
𝒴
(4)
.
Sus códigos se pueden mostrar ejecutando:
for(let i = 0; i < '𝒳𝒴'.length; i++) {
alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500
};
Entonces, el ejemplo anterior encuentra y muestra la mitad izquierda de 𝒳
.
Si agregamos la flag u
, entonces el comportamiento será correcto:
alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳
Ocurre una situación similar cuando se busca un rango, como[𝒳-𝒴]
.
Si olvidamos agregar la flag u
, habrá un error:
'𝒳'.match(/[𝒳-𝒴]/); // Error: Expresión regular inválida
La razón es que sin la bandera u
los pares sustitutos se perciben como dos caracteres, por lo que [𝒳-𝒴]
se interpreta como [<55349><56499>-<55349><56500>]
(cada par sustituto se reemplaza con sus códigos). Ahora es fácil ver que el rango 56499-55349
es inválido: su código de inicio 56499
es mayor que el último 55349
. Esa es la razón formal del error.
Con la bandera u
el patrón funciona correctamente:
// buscar caracteres desde 𝒳 a 𝒵
alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴