6 Desvendando o JavaScript expressão regular #iniciante

Tempo de leitura: 13 minutos

Continuando nossa série desvendando o JavaScript, damos inicio ao nosso sexto post, se você ainda você não leu os posts anteriores volte e leia!! irá ajudar você a compreender os conceitos que serão abordados e conhecer um pouco mais sobre a história do JavaScript

Vamos agora falar um pouco mais sobre Expressão Regular no mundo JavaScript , os conceitos mais usados e como eles funcionam.

Expressão Regular

Uma expressão regular (também chamada de regex ) é uma maneira de trabalhar com strings, de uma maneira muito eficiente.

Ao formular uma expressão regular com uma sintaxe especial, você pode:

  • Pesquisar texto uma string;
  • Substituir partes em uma string;
  • Extrair informações de uma string;

Quase toda linguagem de programação implementa expressões regulares. Existem pequenas diferenças entre cada implementação, mas os conceitos gerais se aplicam em quase todas as linguagens.

As Expressões Regulares datam dos anos 1950, quando foram formalizadas como um padrão de busca conceitual para algoritmos de processamento de strings.

Implementadas em ferramentas UNIX como grep, sed e em editores de texto populares, as regex cresceram em popularidade e foram introduzidas na linguagem de programação Perl e, posteriormente, em muitas outras.

JavaScript, assim como Perl, é uma das linguagens de programação que têm suporte a expressões regulares diretamente embutidas na linguagem.

Difícil mas útil

Expressões regulares podem parecer absurdas para um iniciante, e muitas vezes também para o desenvolvedor profissional, se não se investe o tempo necessário para entendê-las.

Expressões regulares enigmáticas são difíceis de escrever , difíceis de ler e difíceis de manter/modificar .

Mas às vezes uma expressão regular é a única maneira sensata de realizar alguma manipulação de string, por isso é uma ferramenta muito valiosa no seu bolso.

Este post tem como objetivo apresentar as Expressões Regulares do JavaScript de forma simples e fornecer todas as informações para ler e criar expressões regulares.

A regra geral é que expressões regulares simples são fáceis de ler e escrever, enquanto expressões regulares complexas podem rapidamente se tornar uma bagunça se você não entender profundamente o básico.

Como é uma expressão regular?

Em JavaScript, uma expressão regular é um objeto , que pode ser definido de duas maneiras.

O primeiro é instanciando um novo objeto RegExp usando o construtor:

const re1 = new RegExp('hey')

O segundo está usando o formulário literal de expressão regular :

const re1 = /hey/

Você sabe que o JavaScript tem literais de objeto e literais de matriz ? Também tem literais de regex .

No exemplo acima, hey é a forma padrão do formato literal. Na forma literal, a expressão é delimitada por barras, enquanto que com o construtor de objetos, não é.

Esta é a primeira diferença importante entre as duas formas, mas veremos outras mais tarde.

Como funciona?

A expressão regular que definimos acima é muito simples. Ela pesquisa a string hey, sem qualquer limitação.

Você pode testar o regex usando RegExp.test(String), que retorna um booleano:

re1.test('hey')                     // true
re1.test('blablabla hey blablabla') // true

re1.test('he')        // false
re1.test('blablabla') // false

No exemplo acima, apenas verificamos se hey satisfaz o padrão da expressão regular armazenado em re1.

Ancoragem

/hey/

Combina hey onde quer que tenha sido colocado dentro da string.

Se você quiser combinar strings que começam com hey, use o operador ^:

/^hey/.test('hey')     // true
/^hey/.test('bla hey') // false

Se você quiser combinar strings que terminam com hey, use o operador $:

/hey$/.test('hey')     // true
/hey$/.test('bla hey') // true
/hey$/.test('hey you') // false

Junte esses dois operadores e combine strings que correspondam exatamente a hey:

/^hey$/.test('hey') // true  

Para corresponder a uma string que começa com uma substring e termina com outra, você pode usar .*, que corresponde a qualquer caractere repetido 0 ou mais vezes:

/^hey.*joe$/.test('hey joe')             // true
/^hey.*joe$/.test('heyjoe')              // true
/^hey.*joe$/.test('hey how are you joe') // true
/^hey.*joe$/.test('hey joe!')            // false

Corresponder itens em intervalos

Em vez de corresponder a uma string específica, você pode escolher combinar qualquer caractere em um intervalo, como:

/[a-z]/ //a, b, c, ... , x, y, z
/[A-Z]/ //A, B, C, ... , X, Y, Z
/[a-c]/ //a, b, c
/[0-9]/ //0, 1, 2, 3, ... , 8, 9

Esses regexes correspondem a cadeias que contêm pelo menos um dos caracteres nesses intervalos:

/[a-z]/.test('a')  // true
/[a-z]/.test('1')  // false
/[a-z]/.test('A')  // false

/[a-c]/.test('d')  // false
/[a-c]/.test('dc') // true

Os intervalos podem ser combinados:

/[A-Za-z0-9]/
/[A-Za-z0-9]/.test('a') // true
/[A-Za-z0-9]/.test('1') // true
/[A-Za-z0-9]/.test('A') // true

Correspondendo um item de intervalo várias vezes

Você pode verificar se uma string contém um único caractere em um intervalo, usando o caractere :

/^[A-Za-z0-9]$/

/^[A-Za-z0-9]$/.test('A')  // true
/^[A-Za-z0-9]$/.test('Ab') // false

Negando um padrão

O caractere  ^ no início de uma expressão usado dentro de um intervalo, representa uma negação , então:

/[^A-Za-z0-9]/.test('a') // false
/[^A-Za-z0-9]/.test('1') // false
/[^A-Za-z0-9]/.test('A') // false
/[^A-Za-z0-9]/.test('@') // true

Meta characters

  • d corresponde a qualquer dígito, equivalente a 0-9;
  • D corresponde a qualquer caractere que não seja um dígito, equivalente a [^0-9]();
  • w corresponde a qualquer caractere alfanumérico (mais sublinhado), equivalente a A-Za-z_0-9;
  • W corresponde a qualquer caractere não alfanumérico, qualquer coisa, exceto [^A-Za-z_0-9]();
  • s corresponde a qualquer caractere de espaço em branco: espaços, tabulações, novas linhas e espaços Unicode;
  • S corresponde a qualquer caractere que não seja um espaço em branco;
  • 0 corresponde a null;
  • n corresponde a um caractere de nova linha;
  • t corresponde a um caractere de tabulação;
  • uXXXX corresponde a um caractere unicode com o código XXXX );
  • . corresponde a qualquer caractere que não seja um caractere de nova linha (por exemplo n) (a menos que você use o sinalizador, explicado posteriormente)
  • ^corresponde a qualquer caractere, incluindo caracteres de nova linha. É útil em cadeias de múltiplas linhas;

Opções de expressões regulares

Se você quiser pesquisar parte de uma string , use o operador |.

/hey|ho/.test('hey') // true
/hey|ho/.test('ho')  // true

Quantificadores

Digamos que você tenha essa regex, que verifica se uma string tem um dígito e nada mais:

/^\d$/

Você pode usar o quantificador ? para torná-lo opcional, exigindo portanto, zero ou um:

/^\d?$/

Mas e se você quiser combinar vários dígitos?

Você pode fazê-lo de 4 maneiras, usando + , , {n} e {n,m}. 

+ Corresponde a um ou mais itens (> = 1);

/^\d+$/

/^\d+$/.test('12')     // true
/^\d+$/.test('14')     // true
/^\d+$/.test('144343') // true
/^\d+$/.test('')       // false
/^\d+$/.test('1a')     // false

* Corresponde a 0 ou mais (> = 0) itens;

/^\d+$/

/^\d*$/.test('12')     // true
/^\d*$/.test('14')     // true
/^\d*$/.test('144343') // true
/^\d*$/.test('')       // true
/^\d*$/.test('1a')     // false

{n} Corresponde a vários itens;

/^\d{3}$/

/^\d{3}$/.test('123')  // true
/^\d{3}$/.test('12')   // false
/^\d{3}$/.test('1234') // false

/^[A-Za-z0-9]{3}$/.test('Abc') // true

{n,m} Corresponde a um conjunto de 1 há mais números:

/^\d{3,5}$/

/^\d{3,5}$/.test('123')    // true
/^\d{3,5}$/.test('1234')   // true
/^\d{3,5}$/.test('12345')  // true
/^\d{3,5}$/.test('123456') // false

m pode ser omitido para ter um final aberto para ter pelo menos n itens:

/^\d{3,}$/

/^\d{3,}$/.test('12')        // false
/^\d{3,}$/.test('123')       // true
/^\d{3,}$/.test('12345')     // true
/^\d{3,}$/.test('123456789') // true

Itens opcionais

Seguir um item com o ? o torna opcional:

/^\d{3}\w?$/

/^\d{3}\w?$/.test('123')   // true
/^\d{3}\w?$/.test('123a')  // true
/^\d{3}\w?$/.test('123ab') // false

Grupos

Usando parênteses, você pode criar grupos de caracteres: (…)

Este exemplo corresponde exatamente a 3 dígitos seguidos por um ou mais caracteres alfanuméricos:

/^(\d{3})(\w+)$/

/^(\d{3})(\w+)$/.test('123')          // false
/^(\d{3})(\w+)$/.test('123s')         // true
/^(\d{3})(\w+)$/.test('123something') // true
/^(\d{3})(\w+)$/.test('1234')         // true

Caracteres de repetição colocados após um parêntese de fechamento de grupo referem-se ao grupo inteiro:

/^(\d{2})+$/

/^(\d{2})+$/.test('12')   // true
/^(\d{2})+$/.test('123')  // false
/^(\d{2})+$/.test('1234') // true

Capturando Grupos

Até agora, vimos como testar strings e verificar se elas contêm um determinado padrão.

Um recurso muito interessante de expressões regulares é a capacidade de capturar partes de uma string e colocá-las em uma matriz.

Você pode fazer isso usando Grupos e, em particular, grupos de captura. Por padrão, um grupo é um grupo de captura. Agora, em vez de usar RegExp.test(String), que apenas retorna um booleano se o padrão for satisfeito, usamos um:

  • String.match(RegExp);
  • RegExp.exec(String);

Eles são exatamente iguais e retornam um Array com toda a string correspondente no primeiro item, depois cada conteúdo do grupo correspondente.

Se não houver correspondência, ele retorna null:

'123s'.match(/^(\d{3})(\w+)$/)
//Array [ "123s", "123", "s" ]

/^(\d{3})(\w+)$/.exec('123s')
//Array [ "123s", "123", "s" ]

'hey'.match(/(hey|ho)/)
//Array [ "hey", "hey" ]

/(hey|ho)/.exec('hey')
//Array [ "hey", "hey" ]

/(hey|ho)/.exec('ha!')
//null

Quando um grupo é correspondido várias vezes, somente a última correspondência é colocada na matriz resultante:

'123456789'.match(/(\d)+/)
//Array [ "123456789", "9" ]

Grupos opcionais

Um grupo de captura pode ser feito opcional usando (…)?. Se não for encontrado, o slot da matriz resultante conterá undefined:

/^(\d{3})(\s)?(\w+)$/.exec('123 s') //Array [ "123 s", "123", " ", "s" ]
/^(\d{3})(\s)?(\w+)$/.exec('123s') //Array [ "123s", "123", undefined, "s" ]

Referência de grupos correspondentes

Cada grupo que é correspondido é atribuído a um número. $1 refere-se ao primeiro, $2 ao segundo e assim por diante. Isso será útil quando falarmos mais tarde sobre a substituição de partes de uma string.

Grupos de Captura Nomeados

Este é um novo recurso do ES2018 . Um grupo pode ser atribuído a um nome, em vez de apenas ser atribuído um slot na matriz resultante:

const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const result = re.exec('2015-01-02')

// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';

Usando correspondência e exec sem grupos

Há uma diferença em usar matche execsem grupos: o primeiro item na matriz não é a string inteira correspondente, mas a correspondência diretamente:

/hey|ho/.exec('hey')   // [ "hey" ]

/(hey).(ho)/.exec('hey ho') // [ "hey ho", "hey", "ho" ]

Grupos de Não Captura

Como, por padrão, grupos são grupos de captura, você precisa de uma maneira de ignorar alguns grupos na matriz resultante. Isso é possível usando grupos sem captura , que começam com um (?:…)

'123s'.match(/^(\d{3})(?:\s)(\w+)$/)
//null
'123 s'.match(/^(\d{3})(?:\s)(\w+)$/)
//Array [ "123 s", "123", "s" ]

Flags

Você pode usar os seguintes sinalizadores em qualquer expressão regular:

  • g: corresponde ao padrão várias vezes;
  • i: torna o caso de regex insensível;
  • m: ativa o modo de múltiplas linhas. Neste modo, ^ e $ corresponde ao início e ao final da string inteira. Sem isso, com strings de múltiplas linhas elas combinam o início e o fim de cada linha;
  • u: ativa o suporte para unicode (introduzido no ES6 / ES2015);
  • s: (novo no ES2018 ) abreviação de linha única , faz com que o mesmo .coincida com os novos caracteres de linha;

Flags podem ser combinados e são adicionados no final da string em literais regex:

/hey/ig.test('HEy') // true

ou como o segundo parâmetro com os construtores de objetos RegExp:

new RegExp('hey', 'ig').test('HEy') // true

Inspecionando um regex

Dado um regex, você pode inspecionar suas propriedades:

  • source a string padrão;
  • multiline é true com a flag m;
  • global é true com a flag g;
  • ignoreCase true com a flag i ;
  • lastIndex;
/^(\w{3})$/i.source     //"^(\\d{3})(\\w+)$"
/^(\w{3})$/i.multiline  //false
/^(\w{3})$/i.lastIndex  //0
/^(\w{3})$/i.ignoreCase //true
/^(\w{3})$/i.global     //false

Escaping

Esses são os caracteres especiais de escaping:

  • \
  • /
  • ( )
  • { }
  • ?
  • +
  • * – |
  • .
  • ^
  • $

Eles são especiais porque são caracteres de controle que têm um significado no padrão de expressão regular, portanto, se você quiser usá-los dentro do padrão como caracteres correspondentes, será necessário evitá-los, ao incluir uma barra invertida:

/^\\$/
/^\^$/ // /^\^$/.test('^') true
/^\$$/ // /^\$$/.test('$') true

String boundaries

b e B deixará você inspecionar se uma string está no começo ou no final de uma palavra:

  • b corresponde a um conjunto de caracteres no início ou no final de uma palavra;
  • B corresponde a um conjunto de caracteres que não estão no início nem no final de uma palavra;
'I saw a bear'.match(/\bbear/)    //Array ["bear"]
'I saw a beard'.match(/\bbear/)   //Array ["bear"]
'I saw a beard'.match(/\bbear\b/) //null
'cool_bear'.match(/\bbear\b/)     //null

Substituindo usando expressões regulares

Já vimos como verificar se uma string contém um padrão. Também vimos como extrair partes de uma string para uma matriz, correspondendo a um padrão. Agora vamos ver como substituir partes de uma string com base em um padrão.

O objeto String em JavaScript tem um método replace(), que pode ser usado sem expressões regulares para executar uma única substituição em uma string:

"Hello world!".replace('world', 'dog') //Hello dog!
"My dog is a good dog!".replace('dog', 'cat') //My cat is a good dog!

Este método também aceita uma expressão regular como argumento:

"Hello world!".replace(/world/, 'dog') //Hello dog!

Usar o g como sinalizador é a única maneira de substituir várias ocorrências em uma string no JavaScript tradicional:

"My dog is a good dog!".replace(/dog/g, 'cat') //My cat is a good cat!

Grupos nos permitem fazer coisas mais extravagantes, como mover partes de uma string:

"Hello, world!".replace(/(\w+), (\w+)!/, '$2: $1!!!')
// "world: Hello!!!"

Em vez de usar uma string, você pode usar uma função para fazer coisas mais extravagantes. Ele receberá um número de argumentos como o retornado por String.match(RegExp) ou RegExp.exec(String), com vários argumentos que dependem do número de grupos:

"Hello, world!".replace(/(\w+), (\w+)!/, (matchedString, first, second) => {
  console.log(first);
  console.log(second);

  return `${second.toUpperCase()}: ${first}!!!`
})
//"WORLD: Hello!!!"

No próximo post da serie abordaremos  Statement e Herança este será o ultimo da série Desvendando o JavaScript será abordado conceitos práticos e fundamentos, as boas e más práticas de programação.

gostou do conteúdo? não deixe de seguir a uebile nas redes sociais, pois toda semana tem post novo aqui no blog com mais dicas para o seu impulso digital.