Skip to content

Latest commit

 

History

History
198 lines (126 loc) · 8.01 KB

File metadata and controls

198 lines (126 loc) · 8.01 KB

Conjuntos e alcances [...]

Caracteres ou classes de caracteres dentro de colchetes […] significam "Corresponda com qualquer caractere dentre os fornecidos".

Conjuntos

O padrão pattern:[eao], por exemplo, corresponde com qualquer um dos 3 caracteres: 'a', 'e', or 'o'.

Isso é chamado de conjunto. Conjuntos podem ser usados numa regex como qualquer outro caractere normal:

// Case [t ou m], e então "op"
alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top"

Repare que mesmo que um conjunto possua múltiplos caracteres, ele corresponde a apenas um caractere por vez.

Dessa maneira, o exemplo abaixo não corresponde com nada:

// Case com "V", depois [o ou i], e então "la"
alert( "Voila".match(/V[oi]la/) ); // null, nenhuma correspondência

O padrão está procurando por:

  • pattern:V,
  • seguido de apenas uma das letras pattern:[oi],
  • seguido de pattern:la.

Então encontraríamos match:Vola ou match:Vila.

Alcances

Colchetes também podem conter alcances de caracteres.

Por exemplo, pattern:[a-z] é qualquer caractere entre a e z, e pattern:[0-5] é qualquer dígito entre 0 e 5.

No exemplo abaixo estamos buscado por um "x" seguido de dois dígitos ou letras de A a F:

alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF

O padrão pattern:[0-9A-F] tem dois alcances: Ele corresponde com dígitos de 0 a 9 ou uma letra de A a F.

Se quisermos encontrar letras minúsculas também, podemos adicionar o alcance a-f (pattern:[0-9A-Fa-f]), ou adicionar a opção pattern:i.

Também podemos usar classes de caracteres dentro do […].

Se quisermos, por exemplo, buscar por um caractere "de palavra" pattern:\w ou um hífen pattern:-, o conjunto fica pattern:[\w-]

Também é possível combinar várias classes; o padrão pattern:[\s\d], por exemplo, significa "um caractere de espaço ou um dígito".

Por exemplo:

- **\d** -- é o mesmo padrão que `pattern:[0-9]`,
- **\w** -- é o mesmo padrão que `pattern:[a-zA-Z0-9_]`,
- **\s** -- é o mesmo padrão que `pattern:[\t\n\v\f\r ]`, com a adição de mais alguns caracteres Unicode de espaço raros.

Exemplo: \w multilinguagens

Como a classe de caracteres pattern:\w é um atalho para pattern:[a-zA-Z0-9_], ele não reconhece ideogramas, letras cirílicas, etc.

Nós podemos escrever um padrão mais universal, que encontra caracteres usados em palavras de qualquer língua. Fica fácil usando propriedades Unicode: pattern:[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}].

Vamos decifrar o padrão. Similarmente ao pattern:\w, estamos criando nosso próprio conjunto que inclui caracteres com as seguintes propriedades Unicode:

  • Alphabetic (Alpha) - para letras,
  • Mark (M) - para acentos,
  • Decimal_Number (Nd) - para dígitos,
  • Connector_Punctuation (Pc) - para o underscore '_' e caracteres similares,
  • Join_Control (Join_C) - para dois códigos especiais, 200c e 200d, usados em ligaturas Árabes, por exemplo.

Vejamos um caso de uso:

let regexp = /[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]/gu;

let str = `Hi 你好 12`;

// Encontra todas as letras e dígitos:
alert( str.match(regexp) ); // H,i,你,好,1,2

E claro, podemos mudar esse padrão adicionando ou removendo propriedades Unicode. Propriedades Unicode são discutidas em maior profundidade no artigo info:regexp-unicode.

Propriedades Unicode `pattern:p{…}` não são implementadas no Internet Explorer. Se for realmente necessário dar suporte ao navegador, pode-se usar uma biblioteca como a [XRegExp](https://xregexp.com/).

Outra opção é utilizar alcances de caracteres da língua relevante, como `pattern:[а-я]` para o alfabeto cirílico, por exemplo.

Alcances negados

Além dos alcances normais, existem os alcances negados que usam a sintaxe pattern:[^…].

Eles são demarcados pelo acento circunflexo ^ no começo e correspondem a qualquer caractere exceto os incluídos no alcance

Por exemplo:

  • pattern:[^aeyo] -- reconhece qualquer caractere exceto 'a', 'e', 'y' e 'o'.
  • pattern:[^0-9] -- reconhece qualquer caractere exceto um dígito, equivalente ao pattern:\D.
  • pattern:[^\s] -- reconhece qualquer caractere que não seja um espaço em branco, equivalente ao \S.

O exemplo abaixo busca por qualquer caractere que não seja uma letra, um dígito ou um espaço:

alert( "[email protected]".match(/[^\d\sA-Z]/gi) ); // @ e .

Escapes dentro do […]

Normalmente quando queremos encontrar um caractere especial precisamos escapá-lo com a contrabarra pattern:\.. Se precisamos buscar uma contrabarra, escapamos ela também (pattern:\\), e assim por diante

Dentro de colchetes podemos usar a grande maioria de caracteres especiais sem nenhum escape:

  • Os símbolos pattern:. + ( ) nunca precisam de escape.
  • Um hífen pattern:- não precisa ser escapado se estiver no começo ou no final do conjunto(onde ele não define nenhum alcance).
  • O acento circunflexo pattern:^ só precisa ser escapado caso seja o primeiro elemento do conjunto (onde ele sinaliza a negação do conjunto).
  • O colchete direito pattern:] sempre deve ser escapado (caso precisemos buscar por ele).

Em outras palavras, todos os caracteres especiais podem ser usados sem escapes, exceto nos casos onde eles modificam o comportamento do conjunto em si.

O ponto . dentro de um conjunto representa um ponto literal. O padrão pattern:[.,] reconhece um ponto ou uma vírgula.

No exemplo abaixo, a expressão pattern:[-().^+] reconhece qualquer um dos caracteres -().^+:

// Não é necessário escapar nada
let regexp = /[-().^+]/g;

alert( "1 + 2 - 3".match(regexp) ); // Encontra + e -

...Mas caso você queira escapar "só para garantir", o efeito é o mesmo:

// Tudo escapado
let regexp = /[\-\(\)\.\^\+]/g;

alert( "1 + 2 - 3".match(regexp) ); // Também encontra + e -

Alcances e a opção "u"

Se existem pares substitutos no conjunto, a opção pattern:u é obrigatória para garantir seu funcionamento correto.

No exemplo abaixo queremos realizar uma busca pelo padrão pattern:[𝒳𝒴] na string subject:𝒳:

alert( '𝒳'.match(/[𝒳𝒴]/) ); // Mostra um caractere estranho, como um [?]
// (A pesquisa não teve sucesso, retornamos apenas metade de um caractere)

O resultado está errado, já que a a expressão regular "não enxerga" o par substituto.

O interpretador de expressões regulares pensa que o conjunto [𝒳𝒴] contém quatro caracteres, não dois:

  1. Metade esquerda do 𝒳 (1),
  2. Metade direita do 𝒳 (2),
  3. Metade esquerda do 𝒴 (3),
  4. Metade direita do 𝒴 (4).

Podemos ler seus códigos dessa maneira:

for(let i=0; i<'𝒳𝒴'.length; i++) {
  alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500
};

Por conta disso, o exemplo acima reconhece apenas a metade esquerda do 𝒳.

Agora, se adicionarmos a opção pattern:u, o comportamento é o esperado:

alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳

Uma situação parecida acontece quando estamos buscando por um alcance, como [𝒳-𝒴].

Se não usarmos a opção pattern:u, um erro ocorre:

'𝒳'.match(/[𝒳-𝒴]/); // Error: Invalid regular expression (Expressão regular inválida)

Isso ocorre porque sem a opção pattern:u, pares substitutos são percebidos como dois caracteres separados, então o alcance [𝒳-𝒴] é interpretado como [<55349><56499>-<55349><56500>] (cada par substituto é substituído pelos seus códigos constituintes). Dessa forma é fácil perceber que o alcance 56499-55349 é inválido: Seu código inicial 56499 é maior que seu código final, 55349, causando o erro.

Com a opção pattern:u, entretanto, o padrão funciona como esperado:

// Case com caracteres entre 𝒳 e 𝒵
alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴