Um Rápido Mergulho em Expressões Regulares

Read in english here.

Olá. Que tal esquecer um pouco a rotina e a realidade e fazer uma viagem ao interior de sua mente? Descobrir conceitos novos, diferentes. Ao voltar, as coisas não serão mais tão normais quanto antes, pois símbolos estranhos farão parte do seu dia-a-dia.

Inspirado pelo funcionamento de seus próprios neurônios, descubra o fascinante mundo das Expressões Regulares.

Apresentando as Expressões Regulares

Então, para podermos começar nossa viagem, nada como uma apresentação do assunto, pois, afinal de contas, o que é Expressão Regular?

Bem resumido, expressão regular é um método formal de se especificar um padrão de texto.

Mais detalhado, é uma composição de símbolos, caracteres com funções especiais, que, agrupados entre sí e com caracteres literais formam uma sequência, uma expressão. Essa expressão é interpretada como uma regra que indicará sucesso ou falha dependendo da entrada de dados. Ou seja, obedecer exatamente a todas as condições da expressão.

Ou como variações aceitas também pode-se afirmar que é:

  • uma maneira de procurar um texto que você não lembra exatamente como é, mas tem idéia das váriações possíveis;

  • uma maneira de um programador especificar padrões complexos que podem ser procurados e casados em uma cadeia de caracteres;

Ou didádicamente falando, é:

  • como um jogo, no começo é difícil, mas após conhecer todas as regras, basta jogar e curtir;

  • como uma receita culinária, com seus ingredientes e uma ordem correta para adicioná-los á mistura.

Acima de tudo, uma expressão regular é divertida.

História

Em 1968, o pioneiro UNIX *Ken Thompson *publicou um artigo chamado Regular Expression Search Algorithm. A forma que ele descreveu uma Expressão Regular acabou criando o IBM 7094. E não parou por ai, ele também implementou a notação de Kleene em um editor de texto chamado ED.

Para procurar com uma expressão regular no ED, você precisa digitar g/< regular expression>/p. A letra g significa busca global e a letra p significa que você quer imprimir esse resultado. O comando — g/re/p resultou no programa chamado grep, publicado na quarta edição do UNIX em 1973. No entanto, grep não tinha uma completa implementação de expressões regulares até 1979, na sétima edição do UNIX que veio o egrep de Alfred Aho, egrep quer dizer extended grep.

Outros filhos como o sed e o awk também apareceram, cada um implementando as expressões do seu próprio jeito; e finalmente em 1986 foi criado o divisor de águas, um pacote pioneiro em C chamado regex que tratava das expressões e qualquer um poderia inclui-lo no seu próprio programa, de graça. Com isso as expressões caíram no gosto popular e cada vez mais e mais programas e linguagens as utilizam.

*Algumas pessoas, quando confrontada com um problema, pensam: “Eu sei, terei que usar Expressão Regular”. Agora elas passam a ter dois problemas. Jamie Zawinski*Algumas pessoas, quando confrontada com um problema, pensam: “Eu sei, terei que usar Expressão Regular”. Agora elas passam a ter dois problemas. Jamie Zawinski

É totalmente entendível que se você não pratica regex. Em um periodo de tempo você esquecerá muitas coisas relacionadas. A maioria de nós não usamos diariamente, mas ajudará em vários casos quando você precisar usa-las.

Expressão Regular é ao mesmo tempo estranha e extremamente útil. No entanto, é uma ferramenta poderosa para inspeção de string. Compreendendo corretamente Expressões Regulares vai fazer de você um programador mais eficaz.

A notação

Se você está usando Expressão Regular em uma linguagem de programação. No JavaScript por exemplo, uma expressão é um tipo de objeto. Mas em muitos casos você pode criar uma nova expressão simplesmente digitando /< regex>/.

/abc/

O padrão aparece entre os caracteres de barras, caracteres de barra invertida são um pouco diferente. Visto que uma barra normal finaliza uma expressão, para usarmos uma barra, teriamos que colocar uma barra invertida junto de uma barra para poder ser parte do padrão. Além disso, as barras invertidas que não fazem parte do código de caracteres especiais como \n serão preservadas.

Alguns caracteres como ponto de interrogação, sinal de mais, interrogação, entre outros tem significado especial nas Expressões Regulares e deve ser precedido por uma barra invertida, as barras invertida foram feitas para representar o caractere em sí.

/eighteen\+/

Sabendo precisamente o que o caractere de barra invertida irá escapar quando você estiver escrevendo sua expressão irá requerer que você saiba cada caractere e seus significados antes de escapá-los.

Por enquanto, isso ainda não é um problema. Em caso de dúvida, basta colocar uma barra invertida antes de qualquer caractere que não é uma letra, número ou espaço em branco.

Testando por combinação

É muito fácil montar sua expressão procurando exatamente o que você quer. Primeiro você digita a String que gostaria e vai melhorando ela com conhecimento de regex, dessa forma, passo-a-passo.

Um site que recomendo a você usar e testar suas futuras expressões é o regex101.com. Você coloca sua string no campo abaixo e monta sua expressão, logo acima ele mostra os resultados em forma de highlight e ainda informa o que cada caractere faz.

Existe alguns caracteres que são fora das expressões, eles são chamados de modificadores. Mas não trataremos nesse post. Poderemos ter um post especial só para falar de modificadores em Expressões Regulares.

/[123456789]+/ # "In 1992" #=> 1992
/[0-9]+/ # "In 1992" #=> 1992
/\d+/ # "In 1992" #=> 1992

Como visto acima, queremos pegar apenas o padrão 1992. Assim, basicamente passamos os números para casar o que realmente queremos. No entanto, como vemos acima, vamos melhorando a nossa expressão para ela poder ficar menor e mais entendível.

O caractere — significa um range. Onde passamos o inicio e o fim e ele irá retornar apenas o necessário. O caractere [0–9] como é um range de 0 até 9 irá cobrir o 1992. O caracter [] é uma lista, assim podemos passar uma lista do que queremos. Nesse caso apenas números.

Existem uma lista de atalhos de caracteres. De modo que \d significa a mesma coisa que [0–9].

  • \d — Qualquer caractere de número

  • \w — Qualquer caractere alfanumérico (“letra”)

  • \s — Qualquer espaço, tab, nova linha e similares

  • \D — Qualquer caractere que não seja um número

  • \W — Qualquer caractere que não seja um alfanumérico

  • \S — Um caractere que não seja uma linha

  • . — Qualquer caractere exceto new line

Com base nisso, podemos fazer uma expressão para pegar uma data.

/\d\d-\d\d-\d\d\d\d \d\d:\d\d/ #=> 29-01-2016 15:20

Essa expressão parece estranha, não? Com muito caractere de barra invertida, podemos melhora-la usando o range {}.

/(\d{1,2}-\d{1,2}-\d{4}) (\d{1,2}:\d{2})/ #=> 07-02-2016 15:20

Não tenha medo da expressão acima! Ela está um pouco maior que a anterior, mas está bem mais organizada. Agora fazemos uso do metacaractere (), que significa grupo. Assim pegando pelo grupo podemos deixar ela mais organizada. Vamos analizar a seguinte expressão por partes:

  1. Temos o primeiro grupo que pega a data (\d{1,2}-\d{1,2}-\d{4})

  2. Logo após temos um espaço entre a data e a hora

  3. Agora pegamos a hora completa (\d{1,2}:\d{1,2})

  4. Incluindo no inicio a / para abrir a expressão e no final também para finalizar.

Esses caracteres também podem ser usados dentro de uma lista. Por exemplo [\d.] que significa qualquer digito e um ponto. Nota, o caractere . quando usado dentro de uma lista perde seu significado especial, passando a ser apenas um ponto. O mesmo vale para outros caracteres especiais como o +.

Para inverter o que você deseja, basta usar um ^ dentro da lista, ele diz que aceitamos qualquer coisa exceto o que definimos após ele dentro da lista. Um exemplo abaixo, aceita qualquer caractere que não seja número.

/[^\d]/

Lembrando que essa expressão acima irá casar apenas um caractere. Para repetir precisariamos usar o metacaratere +. Veremos logo abaixo.

Repetindo partes de um padrão

Nós sabemos como pegar apenas um digito. E caso nós queremos pegar todo o número? Ou uma sequência de um ou mais digitos?

Quando você coloca um + no final de um padrão, quer dizer que você quer repetir aquele padrão mais de uma vez. Fazendo /[\d]+/ quer dizer que você quer um ou mais caracteres.

/\d+/ # 123 #=> true
/\d+/ # '' #=> false
/\d*/ # 123 #=> true
/\d*/ # '' #=> true

O caracter * diz que pode casar zero ou mais vezes aquele padrão. O que quer dizer que caso queremos evitar um caracter, a * irá sempre casar mesmo não havendo evidências.

Fazendo caracteres opcionais

Uma interrogação faz com que algumas partes de um padrão fiquem opcionais. Isso quer dizer que pode ocorrer zero vezes ou uma vez. No caso abaixo, o u pode ocorrer uma ou nenhuma vez, quando não temos o u ele retorna sucesso.

/neighbou?r/ # neighbor #=> true

Agrupando subexpressões

Para usar um operador do tipo * ou +. Você consegue usar também com um parenthesis. Um padrão que está dentro de um parenthesis quer dizer que ocorrerá apenas uma vez. E para repetir, apenas usamos o caracter +.

/boo+(hoo+)+/i #=> Boohoooohoohooo

Os primeiros caracteres + são para identificar o padrão a esquerda. No primeiro caso ele retorna boo+, o ultimo o irá se repetir. O mesmo acontece com o hoo. No entanto, o ultimo + diz que é para pegar todo o grupo de hoo e repetir.

O i no final da expressão é um modifier, ele informa que não importa se o padrão vem em UpperCase ou LowerCase. Ele irá retornar caso venha com B ou b.

Escolhendo padrões

Então agora sabemos como pegar um padrão e se precisar repetir ele. Mas em casos onde depende da string para vir? A texto passado pode ser vaca, mesa ou email. Nesse caso nós fazemos o uso do caractere |.

/\b\d+ (vaca|mesa|email)s?\b/

E também fazemos o usa aqui dos parenthesis, que podem ser usados para agrupar o que queremos e no final ainda deixar o s opcional. Nesse caso iremos casar com vaca ou mesa ou email.

O mecanismo de uma combinação

Expressões Regulares podem seguir um diagrama. O diagrama abaixo foi removido do livro Eloquent Javascript. Que está disponivel traduzido para você ler. E recomendo fortemente que você leia a seção de Expressão Regular, onde aborda como você usa com JavaScript. O exemplo passado é este:

/\b\d+ (ping|cow|chicken)s?\b/
"15 pigs" #=> true
"15 pigchickens" #=> false

Uma string corresponde à expressão se um caminho do início (esquerda) até o final (direita) do diagrama puder ser encontrado, com uma posição inicial e final correspondente, de modo que cada vez que passar em uma caixa, verificamos que a posição atual na seqüência corresponde ao elemento descrito nela, e, para os elementos que correspondem caracteres reais (menos os limites de palavra), continue no fluxo das caixas.

Então se encontrarmos “the 3 pigs” existe uma correspondência entre as posições 4 (o dígito “3”) e 10 (o final da string).

  • Na posição 4, existe um limite de palavra, então passamos a primeira caixa

  • Ainda na posição 4, encontramos um dígito, então ainda podemos passar a primeira caixa.

  • Na posição 5, poderíamos voltar para antes da segunda caixa (dígitos), ou avançar através da caixa que contém um único caractere de espaço. Há um espaço aqui, não um dígito, por isso escolhemos o segundo caminho.

  • Estamos agora na posição 6 (o início de “porcos”) e na divisão entre três caminhos do diagrama. Nós não temos “vaca” ou “galinha” aqui, mas nós temos “porco”, por isso tomamos esse caminho.

  • Na posição 9, depois da divisão em três caminhos, poderíamos também ignorar o “s” e ir direto para o limite da palavra, ou achar o “s” primeiro. Existe um “s”, não um limite de palavra, então passamos a caixa de “s”.

  • Estamos na posição 10 (final da string) e só podemos achar um limite de palavra. O fim de uma string conta como um limite de palavra, de modo que passamos a última caixa e achamos com sucesso a busca.

O modo como o mecanismo de expressões regulares do JavaScript trata uma busca em uma string é simples. Começa no início da string e tenta achar um resultado nela. Nesse casso, existe um limite de palavra aqui, então passamos pela primeira caixa, mas nao existe um dígito, então ele falha na segunda caixa. Continua no segundo caractere da string e tenta novamente. E assim continua, até encontrar um resultado ou alcançar o fim da string e concluir que não encontrou nenhum resultado

Sumário

Expressões regulares são objetos que representam padrões em strings. Eles usam sua própria sintaxe para expressar esses padrões.

/abc/     - Sequência de caracteres
/[abc]/   - Qualquer caractere do conjunto
/[^abc]/  - Qualquer caractere que não seja do conjunto
/[0-9]/   - Qualquer caractere no intervalo de caracteres
/x+/      - Uma ou mais ocorrências do padrão
/x+?/     - Uma ou mais ocorrências do padrão, não obrigatório
/x*/      - Zero ou mais ocorrências
/x?/      - Zero ou uma ocorrência
/x{2,4}/  - Entre duas e quatro ocorrências
/(abc)+/  - Agrupamento
/a|b|c/   - Padrões alternativos
/\d/      - Caracteres dígitos
/\w/      - Caracteres alfanuméricos ("caracteres palavra")
/\s/      - caracteres espaço em branco
/./   - Todos caracteres exceto quebras de linha
/\b/      - Limite de palavra
/^/   - Início da entrada
/$/   - Final da Entrada

Referências

Conclusão

Ficamos por aqui, essa foi uma pequena introdução de Expressões Regulares. Ainda tem muita coisa para aprender e descobrir sobre expressões. Espero que tenha gostado e caso tenha visto algum problema com relação ao texto, não esquece de compartilhar nos comentários.

We are hiring new talents. Do you want to work with us? become@codeminer42.com